summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>2000-02-25 19:08:53 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>2000-02-25 19:08:53 +0000
commit998d769a0cf8bef7d4ca0d26945c151a23b542ec (patch)
treefe53a083eaa06a2bf7631453e18a161a86ad9d62
parentb0226ecd4460819556afd27fd575d64421fd0f68 (diff)
initial import of mg2a
-rw-r--r--usr.bin/mg/Makefile133
-rw-r--r--usr.bin/mg/README197
-rw-r--r--usr.bin/mg/basic.c444
-rw-r--r--usr.bin/mg/buffer.c584
-rw-r--r--usr.bin/mg/chrdef.h79
-rw-r--r--usr.bin/mg/cinfo.c135
-rw-r--r--usr.bin/mg/def.h312
-rw-r--r--usr.bin/mg/dir.c59
-rw-r--r--usr.bin/mg/dired.c195
-rw-r--r--usr.bin/mg/display.c884
-rw-r--r--usr.bin/mg/dvidoc.sty181
-rw-r--r--usr.bin/mg/echo.c781
-rw-r--r--usr.bin/mg/extend.c797
-rw-r--r--usr.bin/mg/file.c482
-rw-r--r--usr.bin/mg/fileio.c628
-rw-r--r--usr.bin/mg/help.c270
-rw-r--r--usr.bin/mg/kbd.c391
-rw-r--r--usr.bin/mg/kbd.h63
-rw-r--r--usr.bin/mg/key.h13
-rw-r--r--usr.bin/mg/keymap.c1217
-rw-r--r--usr.bin/mg/line.c609
-rw-r--r--usr.bin/mg/macro.c83
-rw-r--r--usr.bin/mg/macro.h23
-rw-r--r--usr.bin/mg/main.c143
-rw-r--r--usr.bin/mg/match.c189
-rw-r--r--usr.bin/mg/mg.1139
-rw-r--r--usr.bin/mg/mg.doc2202
-rw-r--r--usr.bin/mg/mg.dvibin0 -> 74936 bytes
-rw-r--r--usr.bin/mg/mg.tex1345
-rw-r--r--usr.bin/mg/mgidx.tex131
-rw-r--r--usr.bin/mg/mgprog.doc295
-rw-r--r--usr.bin/mg/modes.c138
-rw-r--r--usr.bin/mg/paragraph.c284
-rw-r--r--usr.bin/mg/random.c444
-rw-r--r--usr.bin/mg/re_search.c706
-rw-r--r--usr.bin/mg/regex.c1594
-rw-r--r--usr.bin/mg/regex.h106
-rw-r--r--usr.bin/mg/region.c291
-rw-r--r--usr.bin/mg/search.c664
-rw-r--r--usr.bin/mg/spawn.c120
-rw-r--r--usr.bin/mg/sysdef.h37
-rw-r--r--usr.bin/mg/tar.exclude7
-rw-r--r--usr.bin/mg/tty.c442
-rw-r--r--usr.bin/mg/ttydef.h25
-rw-r--r--usr.bin/mg/ttyio.c265
-rw-r--r--usr.bin/mg/ttykbd.c55
-rw-r--r--usr.bin/mg/tutorial604
-rw-r--r--usr.bin/mg/version.c20
-rw-r--r--usr.bin/mg/window.c399
-rw-r--r--usr.bin/mg/word.c252
50 files changed, 19457 insertions, 0 deletions
diff --git a/usr.bin/mg/Makefile b/usr.bin/mg/Makefile
new file mode 100644
index 00000000000..af0fca401aa
--- /dev/null
+++ b/usr.bin/mg/Makefile
@@ -0,0 +1,133 @@
+# Makefile for MicroEMACS.
+# Is there a better way to do the rebuilds, other than using
+# the links?
+
+SYS = sysv
+LIBS =
+# CDEFS gets defines, and gets passed to lint. CFLAGS gets flags, and doesn't
+# get passed to lint.
+#
+# (Common) compile-time options:
+#
+# DO_METAKEY -- if bit 7 is set for a key, treat like a META key
+# STARTUP -- look for and handle initialization file
+# XKEYS -- use termcap function key definitions. Warning -
+# XKEYS and bsmap mode do _not_ get along.
+# BACKUP -- enable "make-backup-files"
+# PREFIXREGION -- enable function "prefix-region"
+# REGEX -- create regular expression functions
+#
+#CDEFS = -DDO_METAKEY
+CDEFS = -DDO_METAKEY -DPREFIXREGION
+CFLAGS = $(CDEFS)
+CFLAGSNOO = $(CDEFS)
+
+# Objects which only depend on the "standard" includes
+OBJS = basic.o dir.o dired.o file.o line.o match.o paragraph.o \
+ random.o region.o search.o version.o window.o word.o
+
+# Those with unique requirements
+IND = buffer.o display.o echo.o extend.o help.o kbd.o keymap.o \
+ macro.o main.o modes.o regex.o re_search.o
+
+# System dependent objects
+OOBJS = cinfo.o spawn.o ttyio.o tty.o ttykbd.o
+
+OBJ = $(OBJS) $(IND) $(OOBJS) fileio.o
+
+OSRCS = cinfo.c fileio.c spawn.c ttyio.c tty.c ttykbd.c
+SRCS = basic.c dir.c dired.c file.c line.c match.c paragraph.c \
+ random.c region.c search.c version.c window.c word.c \
+ buffer.c display.c echo.c extend.c help.c kbd.c keymap.c \
+ macro.c main.c modes.c regex.c re_search.c
+
+OINCS = ttydef.h sysdef.h chrdef.h
+INCS = def.h
+
+mg: $(OBJ)
+ cc $(CFLAGS) -o mg $(OBJ) $(LIBS)
+
+tar:
+ tar -c -X tar.exclude -f mg.tar .
+
+# strip mg once you're satisfied it'll run -- makes it much smaller
+strip:
+ strip mg
+
+lint: $(SRCS) $(OSRCS) $(INCS) $(OINCS)
+ lint -ahbz $(CDEFS) $(SRCS) $(OSRCS)
+
+# routines that can't be compiled optimized
+# region causes the optimizer to blow up
+# region.o: region.c
+# cc $(CFLAGSNOO) -c region.c
+
+# echo blows up when compiled optimized.
+# echo.o: echo.c
+# cc $(CFLAGSNOO) -c echo.c
+
+$(OBJ): $(INCS) $(OINCS)
+
+
+dir.r search.o: $(INCS) $(OINCS)
+
+regex.o re_search.o: $(INCS) $(OINCS) regex.h
+
+kbd.o: $(INCS) $(OINCS) macro.h kbd.h key.h
+
+macro.o main.o: $(INCS) $(OINCS) macro.h
+
+buffer.o display.o keymap.o help.o modes.o dired.o fileio.o: \
+ $(INCS) $(OINCS) kbd.h
+
+extend.o: $(INCS) $(OINCS) kbd.h macro.h key.h
+
+help.o: $(INCS) $(OINCS) kbd.h key.h macro.h
+
+echo.o: $(INCS) $(OINCS) key.h macro.h
+
+$(OOBJS): $(INCS) $(OINCS)
+
+# sysdef.h: sys/$(SYS)/sysdef.h # Update links, if needed.
+# rm -f sysdef.h
+# ln sys/$(SYS)/sysdef.h .
+
+# ttydef.h: sys/default/ttydef.h
+# rm -f ttydef.h
+# ln sys/default/ttydef.h .
+
+# chrdef.h: sys/default/chrdef.h
+# rm -f chrdef.h
+# ln sys/default/chrdef.h .
+
+# fileio.c: sys/$(SYS)/fileio.c
+# rm -f fileio.c
+# ln sys/$(SYS)/fileio.c .
+
+# spawn.c: sys/$(SYS)/spawn.c
+# rm -f spawn.c
+# ln sys/$(SYS)/spawn.c .
+
+# tty.c: sys/default/tty.c
+# rm -f tty.c
+# ln sys/default/tty.c .
+
+# ttyio.c: sys/$(SYS)/ttyio.c
+# rm -f ttyio.c
+# ln sys/$(SYS)/ttyio.c .
+
+# ttykbd.c: sys/default/ttykbd.c
+# rm -f ttykbd.c
+# ln sys/default/ttykbd.c .
+
+# cinfo.c: sys/default/cinfo.c
+# rm -f cinfo.c
+# ln sys/default/cinfo.c .
+
+# port: $(SRCS) $(INCS)
+# rm -f port
+# tar cfb port 1 $?
+
+# clean:; rm -f $(OBJ) $(OSRCS) $(OINCS)
+clean:; rm -f $(OBJ)
+
diff --git a/usr.bin/mg/README b/usr.bin/mg/README
new file mode 100644
index 00000000000..e37a3c26b06
--- /dev/null
+++ b/usr.bin/mg/README
@@ -0,0 +1,197 @@
+Mg 2a README May 15, 1988
+
+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 most of it does not
+have the copyright restrictions present in GNU Emacs. (However, some
+of the system dependent modules and the regular expression module do
+have copyright notices, specificly the VMS/primos termcap routines and
+the amiga specific routines. Look at the source code for exact
+copyright restrictions.) The Mg authors individually may or may not
+agree with the opinions expressed by Richard Stallman in "The GNU
+Manifesto".
+
+To avoid GNU copyright restrictions, replace the re_search.c, regex.h
+and regex.c files with empty files.
+
+Documentation of Mg is in the TeX file mg.tex. This should be
+formatted with the TeX text formatter and printed. A start twords a mg
+programmers guied in in mgprog.doc, and some of the changes from 1b
+are mentioned briefly in mg2a.change.
+
+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 thing for one
+reason or another. It is compatible with GNU because there shouldn't
+be any reason to learn more than one Emacs flavor. We have excised
+most MicroEMACS features that were incompatible with the big brother,
+and added missing features that seemed essential.
+
+There are at least two other major versions of MicroEMACS in
+circulation. One comes from Daniel Lawrence, (based on an old version
+from Dave Conroy) and is several versions have been posted to usenet.
+It uses a 3.x version numbering scheme, and the latest I know about is
+3.9i. It has some features not found in Mg, missing others, is
+bigger, and is incompatible with GNU Emacs. It might be a better
+choice for you if you *must* have something not present here and can't
+run GNU.
+
+Another variety uses a different numbering scheme, and is up to v30.
+This also comes from mod.sources, and is the latest version from the
+original MicroEMACS author Dave Conroy. Mg is derived from this
+version, and for the most part has replaced it.
+
+Mg is continuing to diverge from other MicroEmacs varients.
+Significant modifacations would me nessisary to adapt code from either
+the 3.x strains or v30. Command functions and key mapping, for
+instance, are completely different.
+
+This is the third distribution release of Mg. (It went through four
+beta releases to iron out the changes made by the various authors.)
+Prior releases were known as MicroGnuEmacs 1a and MicroGnuEmacs 1b.
+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 previos versions of Mg:
+
+ rtech!daveb@sun.com Dave Brower
+
+These systems are known to work in the current version:
+
+ 4.2 & 4.3 BSD Unix, SunOs 3.2, Ultrix-32
+ System V
+ OS9/68k
+ VMS
+ Amiga
+ Primos
+ Atari ST
+
+Ms-Dos support is planned, but did not get done in time for this
+release. (Jeff Siegal <jbs@eddie.mit.edu> was the one doing it.)
+The Ms-Dos files will probably be distributed seperatly when it
+becomes available.
+
+Cpm/68k support was dropped due to compiler bugs. Eunice support was
+dropped because of lack of interest. Mg 1b does support those
+systems.
+
+One change to late to make it into mg.tex is readding bsmap-mode (only
+if BSMAP is #defined when compiling). This is a toggle that controls
+input mapping to exchange the ^H (backspace) and DEL characters. Like
+GNU emacs input keymaps, it is not displayed on the mode line and will
+cause them to be treated as each other for echoing. (With bsmap-mode
+enabled, DEL will echo ^H in the echo line.)
+
+
+How to Make a Mg
+---------------------------
+
+On UNIX at least, it's easy. (Note that even on these systems you may
+want to change a compile time option.) If you have BSD UNIX, do:
+
+ ln sys/bsd/Makefile .
+ make
+
+For System V, do:
+
+ ln sys/sysv/Makefile .
+ make
+
+There are several other directories under sys: osk, vms, amiga, atari,
+prime. You should follow the directions contained therein to make one
+of those versions.
+
+For most systems (everyting except the amiga, and atari currently),
+the termcap terminal definition is used. There is a readme file in
+the default subdirectory of the sys directory explaining what entries
+are used and how. (Termcap is a way to do display manipulation in a
+terminal independent manner.) Besides the normal startup file (usually
+.mg) terminal specific initialization files may be used. (For
+example, in .mg.vt100 you may want to (global-set-key "\e[A"
+'previous-line) to have the up arrow key work.)
+
+Some changes made to make this version more like Gnu Emacs may break
+startup files. Gnu Emacs 18 has both backward-delete-char and
+delete-backward-char that apperently do the same thing. This version
+has only the latter because that is what is documented in my manual
+(version 17) and bound by Gnu Emacs to DEL.
+
+----------------------------------------------------------------------
+
+Known limitaions:
+
+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.
+
+
+
+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.
+
+------------------------------------------------------------------------
+
+If you have a change to make that you think should be incorporated
+into the next version of Mg, send it the mg-support mail
+list. Addresses are:
+
+ mg-support%ais1@ecla.usc.edu
+ {cit-vax,sdcrdcf,trwrb}!oberon!ais1!mg-support
+
+Support for additional systems and terminals should include being
+available for beta testing as other changes are made. (Send a short
+note to mg-support.) Currently, beta test copies of Mg are made
+available via Internet ftp, so beta testers need access to the
+Internet. (UUCP sites that are customers of uunet can get it via
+them. Contact uunet!uunet-request for details.) If you can't reach
+one of us via a computer network, I suppose you could send a change to
+my snail mail address below on 5" os9 format disks or 9 track tape
+(ANSI variable label or Prime magsav format), but this effectivly
+rules you out as a potential beta tester. (Don't expect the disk or
+tape back unless you inculude a SASE with sufficent postage.) I will
+not be sending out copies on magnetic media, so please don't ask. If
+you somehow got an incomplete or non-standard copy, (i.e. missing one
+of the sys directories mentioned here as working) complain to who you
+got it from not to me.
+
+ Robert Larson
+ 309 S. Alexandria Ave.
+ Apt. 117
+ Los Angeles, CA 90020
+
+Alternatively, and under the same conditions, you can send either a 3"
+AmigaDOS format disk or a 9 track tape (Unix tar format) to:
+
+ Mike Meyer
+ P.O. Box 4730
+ Berkeley, CA 94704
+
diff --git a/usr.bin/mg/basic.c b/usr.bin/mg/basic.c
new file mode 100644
index 00000000000..b35e1defeef
--- /dev/null
+++ b/usr.bin/mg/basic.c
@@ -0,0 +1,444 @@
+/*
+ * Basic cursor motion commands.
+ *
+ * The routines in this file are the basic
+ * command functions for moving the cursor around on
+ * the screen, setting mark, and swapping dot with
+ * mark. Only moves between lines, which might make the
+ * current buffer framing bad, are hard.
+ */
+#include "def.h"
+
+VOID setgoal();
+
+/*
+ * Go to beginning of line.
+ */
+/*ARGSUSED*/
+gotobol(f, n)
+{
+ curwp->w_doto = 0;
+ return (TRUE);
+}
+
+/*
+ * Move cursor backwards. Do the
+ * right thing if the count is less than
+ * 0. Error if you try to move back from
+ * the beginning of the buffer.
+ */
+/*ARGSUSED*/
+backchar(f, n)
+register int n;
+{
+ register LINE *lp;
+
+ if (n < 0) return forwchar(f, -n);
+ while (n--) {
+ if (curwp->w_doto == 0) {
+ if ((lp=lback(curwp->w_dotp)) == curbp->b_linep) {
+ if (!(f & FFRAND))
+ ewprintf("Beginning of buffer");
+ return (FALSE);
+ }
+ curwp->w_dotp = lp;
+ curwp->w_doto = llength(lp);
+ curwp->w_flag |= WFMOVE;
+ } else
+ curwp->w_doto--;
+ }
+ return TRUE;
+}
+
+/*
+ * Go to end of line.
+ */
+/*ARGSUSED*/
+gotoeol(f, n)
+{
+ curwp->w_doto = llength(curwp->w_dotp);
+ return (TRUE);
+}
+
+/*
+ * Move cursor forwards. Do the
+ * right thing if the count is less than
+ * 0. Error if you try to move forward
+ * from the end of the buffer.
+ */
+/*ARGSUSED*/
+forwchar(f, n)
+register 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_linep) {
+ curwp->w_dotp = lback(curwp->w_dotp);
+ if (!(f & FFRAND))
+ ewprintf("End of buffer");
+ return FALSE;
+ }
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFMOVE;
+ } else
+ curwp->w_doto++;
+ }
+ return TRUE;
+}
+
+/*
+ * Go to the beginning of the
+ * buffer. Setting WFHARD is conservative,
+ * but almost always the case.
+ */
+gotobob(f, n)
+{
+ (VOID) setmark(f, n) ;
+ curwp->w_dotp = lforw(curbp->b_linep);
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFHARD;
+ return TRUE;
+}
+
+/*
+ * Go to the end of the buffer.
+ * Setting WFHARD is conservative, but
+ * almost always the case.
+ */
+gotoeob(f, n)
+{
+ (VOID) setmark(f, n) ;
+ curwp->w_dotp = lback(curbp->b_linep);
+ curwp->w_doto = llength(curwp->w_dotp);
+ curwp->w_flag |= WFHARD;
+ 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*/
+forwline(f, n)
+{
+ register LINE *dlp;
+
+ if (n < 0)
+ return backline(f|FFRAND, -n);
+ if ((lastflag&CFCPCN) == 0) /* Fix goal. */
+ setgoal();
+ thisflag |= CFCPCN;
+ if (n == 0) return TRUE;
+ dlp = curwp->w_dotp;
+ while (dlp!=curbp->b_linep && n--)
+ dlp = lforw(dlp);
+ curwp->w_flag |= WFMOVE;
+ if(dlp==curbp->b_linep) { /* ^N at end of buffer creates lines (like gnu) */
+ if(!(curbp->b_flag&BFCHG)) { /* first change */
+ curbp->b_flag |= BFCHG;
+ curwp->w_flag |= WFMODE;
+ }
+ curwp->w_doto = 0;
+ while(n-- >= 0) {
+ if((dlp = lallocx(0)) == NULL) return FALSE;
+ dlp->l_fp = curbp->b_linep;
+ dlp->l_bp = lback(dlp->l_fp);
+ dlp->l_bp->l_fp = dlp->l_fp->l_bp = dlp;
+ }
+ curwp->w_dotp = lback(curbp->b_linep);
+ } else {
+ 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*/
+backline(f, n)
+{
+ register LINE *dlp;
+
+ if (n < 0) return forwline(f|FFRAND, -n);
+ if ((lastflag&CFCPCN) == 0) /* Fix goal. */
+ setgoal();
+ thisflag |= CFCPCN;
+ dlp = curwp->w_dotp;
+ while (n-- && lback(dlp)!=curbp->b_linep)
+ dlp = lback(dlp);
+ curwp->w_dotp = dlp;
+ curwp->w_doto = getgoal(dlp);
+ curwp->w_flag |= 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() {
+
+ curgoal = getcolpos() - 1; /* 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.
+ */
+getgoal(dlp) register LINE *dlp; {
+ register int c;
+ register int col;
+ register int newcol;
+ register int dbo;
+
+ col = 0;
+ dbo = 0;
+ while (dbo != llength(dlp)) {
+ c = lgetc(dlp, dbo);
+ newcol = col;
+ if (c == '\t'
+#ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+#endif
+ )
+ newcol |= 0x07;
+ else if (ISCTRL(c) != FALSE)
+ ++newcol;
+ ++newcol;
+ if (newcol > curgoal)
+ break;
+ col = newcol;
+ ++dbo;
+ }
+ return (dbo);
+}
+
+/*
+ * 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*/
+forwpage(f, n)
+register int n;
+{
+ register 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);
+#ifdef CVMVAS
+ else /* Convert from pages */
+ n *= curwp->w_ntrows; /* to lines. */
+#endif
+ lp = curwp->w_linep;
+ while (n-- && lforw(lp)!=curbp->b_linep)
+ lp = lforw(lp);
+ curwp->w_linep = lp;
+ curwp->w_flag |= WFHARD;
+ /* if in current window, don't move dot */
+ for(n = curwp->w_ntrows; n-- && lp!=curbp->b_linep; lp = lforw(lp))
+ if(lp==curwp->w_dotp) return TRUE;
+ curwp->w_dotp = curwp->w_linep;
+ 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*/
+backpage(f, n)
+register int n;
+{
+ register LINE *lp;
+
+ if (!(f & FFARG)) {
+ n = curwp->w_ntrows - 2; /* Default scroll. */
+ if (n <= 0) /* Don't blow up if the */
+ n = 1; /* window is tiny. */
+ } else if (n < 0)
+ return forwpage(f|FFRAND, -n);
+#ifdef CVMVAS
+ else /* Convert from pages */
+ n *= curwp->w_ntrows; /* to lines. */
+#endif
+ lp = curwp->w_linep;
+ while (n-- && lback(lp)!=curbp->b_linep)
+ lp = lback(lp);
+ curwp->w_linep = lp;
+ curwp->w_flag |= WFHARD;
+ /* if in current window, don't move dot */
+ for(n = curwp->w_ntrows; n-- && lp!=curbp->b_linep; lp = lforw(lp))
+ if(lp==curwp->w_dotp) return TRUE;
+ curwp->w_dotp = curwp->w_linep;
+ 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.
+ */
+
+#ifdef GOSMACS
+forw1page(f, n)
+int f, n;
+{
+ if (!(f & FFARG)) {
+ n = 1;
+ f = FFUNIV;
+ }
+ forwpage(f|FFRAND, n);
+}
+
+back1page(f, n)
+int f, n;
+{
+ if (!(f & FFARG)) {
+ n = 1;
+ f = FFUNIV;
+ }
+ backpage(f|FFRAND, n);
+}
+#endif
+
+/*
+ * Page the other window. Check to make sure it exists, then
+ * nextwind, forwpage and restore window pointers.
+ */
+pagenext(f, n)
+{
+ register WINDOW *wp;
+
+ if (wheadp->w_wndp == NULL) {
+ ewprintf("No other window");
+ return FALSE;
+ }
+ wp = curwp;
+ (VOID) nextwind(f, n);
+ (VOID) forwpage(f, n);
+ curwp = wp;
+ curbp = wp->w_bufp;
+ return TRUE;
+}
+
+/*
+ * Internal set mark routine, used by other functions (daveb).
+ */
+VOID
+isetmark()
+{
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+}
+
+/*
+ * 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*/
+setmark(f, n)
+{
+ isetmark();
+ ewprintf("Mark set");
+ 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*/
+swapmark(f, n)
+{
+ register LINE *odotp;
+ register int odoto;
+
+ if (curwp->w_markp == NULL) {
+ ewprintf("No mark in this window");
+ return FALSE;
+ }
+ odotp = curwp->w_dotp;
+ odoto = curwp->w_doto;
+ curwp->w_dotp = curwp->w_markp;
+ curwp->w_doto = curwp->w_marko;
+ curwp->w_markp = odotp;
+ curwp->w_marko = odoto;
+ curwp->w_flag |= 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*/
+gotoline(f, n)
+register int n;
+{
+ register LINE *clp;
+ register int s;
+ char buf[32];
+
+ if (!(f & FFARG)) {
+ if ((s=ereply("Goto line: ", buf, sizeof(buf))) != TRUE)
+ return s;
+ n = atoi(buf);
+ }
+
+ if (n > 0) {
+ clp = lforw(curbp->b_linep); /* "clp" is first line */
+ while (--n > 0) {
+ if (lforw(clp) == curbp->b_linep) break;
+ clp = lforw(clp);
+ }
+ } else {
+ clp = lback(curbp->b_linep); /* clp is last line */
+ while (n < 0) {
+ if (lback(clp) == curbp->b_linep) break;
+ clp = lback(clp);
+ n++;
+ }
+ }
+ curwp->w_dotp = clp;
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFMOVE;
+ return TRUE;
+}
diff --git a/usr.bin/mg/buffer.c b/usr.bin/mg/buffer.c
new file mode 100644
index 00000000000..b4244086bf7
--- /dev/null
+++ b/usr.bin/mg/buffer.c
@@ -0,0 +1,584 @@
+/*
+ * Buffer handling.
+ */
+#include "def.h"
+#include "kbd.h" /* needed for modes */
+
+static RSIZE itor();
+
+/*
+ * 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*/
+usebuffer(f, n)
+{
+ register BUFFER *bp;
+ register int s;
+ char bufn[NBUFN];
+
+ /* Get buffer to use from user */
+ if ((curbp->b_altb == NULL)
+ && ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
+ s=eread("Switch to buffer: ", bufn, NBUFN, EFNEW|EFBUF);
+ else
+ s=eread("Switch to buffer: (default %s) ", bufn, NBUFN,
+ EFNEW|EFBUF, curbp->b_altb->b_bname);
+
+ if (s == ABORT) return s;
+ if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb ;
+ else if ((bp=bfind(bufn, TRUE)) == NULL) return FALSE;
+
+ /* and put it in current window */
+ curbp = bp;
+ return showbuffer(bp, curwp, WFFORCE|WFHARD);
+}
+
+/*
+ * pop to buffer asked for by the user.
+ */
+/*ARGSUSED*/
+poptobuffer(f, n)
+{
+ register BUFFER *bp;
+ register WINDOW *wp;
+ register int s;
+ char bufn[NBUFN];
+ WINDOW *popbuf();
+
+ /* Get buffer to use from user */
+ if ((curbp->b_altb == NULL)
+ && ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
+ s=eread("Switch to buffer in other window: ", bufn, NBUFN,
+ EFNEW|EFBUF);
+ else
+ s=eread("Switch to buffer in other window: (default %s) ",
+ bufn, NBUFN, EFNEW|EFBUF, curbp->b_altb->b_bname);
+ if (s == ABORT) return s;
+ if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb ;
+ else if ((bp=bfind(bufn, TRUE)) == NULL) return FALSE;
+
+ /* and put it in a new window */
+ if ((wp = popbuf(bp)) == NULL) return FALSE;
+ curbp = bp;
+ curwp = wp;
+ return TRUE;
+}
+
+/*
+ * Dispose of a buffer, by name.
+ * Ask for the name. 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*/
+killbuffer(f, n)
+{
+ register BUFFER *bp;
+ register BUFFER *bp1;
+ register BUFFER *bp2;
+ WINDOW *wp;
+ register int s;
+ char bufn[NBUFN];
+
+ if ((s=eread("Kill buffer: (default %s) ", bufn, NBUFN, EFNEW|EFBUF,
+ curbp->b_bname)) == ABORT) return (s);
+ else if (s == FALSE) bp = curbp;
+ else if ((bp=bfind(bufn, FALSE)) == NULL) return FALSE;
+
+ /* 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) {
+ bp1 = (bp == bheadp) ? bp->b_bufp : bheadp;
+ if (bp1 == NULL) {
+ /* only one buffer. see if it's *scratch* */
+ if (bp == bfind("*scratch*",FALSE))
+ return FALSE;
+ /* create *scratch* for alternate buffer */
+ if ((bp1 = bfind("*scratch*",TRUE)) == NULL)
+ return FALSE;
+ }
+ }
+ if (bclear(bp) != TRUE) return TRUE;
+ 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|WFFORCE|WFHARD) != NULL)
+ bp1->b_altb = bp2;
+ else bp1 = bp2;
+ }
+ }
+ if (bp == curbp) curbp = bp1;
+ free((char *) bp->b_linep); /* 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;
+ }
+ free(bp->b_bname); /* Release name block */
+ free((char *) bp); /* Release buffer block */
+ return TRUE;
+}
+
+/*
+ * Save some buffers - just call anycb with the arg flag.
+ */
+/*ARGSUSED*/
+savebuffers(f, n)
+{
+ if (anycb(f) == ABORT) return ABORT;
+ return TRUE;
+}
+
+/*
+ * 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*/
+listbuffers(f, n)
+{
+ register BUFFER *bp;
+ register WINDOW *wp;
+ BUFFER *makelist();
+ WINDOW *popbuf();
+
+ if ((bp=makelist()) == NULL || (wp=popbuf(bp)) == NULL)
+ return FALSE;
+ wp->w_dotp = bp->b_dotp; /* fix up if window already on screen */
+ wp->w_doto = bp->b_doto;
+ return TRUE;
+}
+
+/*
+ * This routine rebuilds the text for the
+ * list buffers command. Return TRUE if
+ * everything works. Return FALSE if there
+ * is an error (if there is no memory).
+ */
+BUFFER *
+makelist() {
+ register char *cp1;
+ register char *cp2;
+ register int c;
+ register BUFFER *bp;
+ LINE *lp;
+ register RSIZE nbytes;
+ BUFFER *blp;
+ char b[6+1];
+ char line[128];
+
+ if ((blp = bfind("*Buffer List*", TRUE)) == NULL) return NULL;
+ if (bclear(blp) != TRUE) return NULL;
+ blp->b_flag &= ~BFCHG; /* Blow away old. */
+
+ (VOID) strcpy(line, " MR Buffer");
+ cp1 = line + 10;
+ while(cp1 < line + 4 + NBUFN + 1) *cp1++ = ' ';
+ (VOID) strcpy(cp1, "Size File");
+ if (addline(blp, line) == FALSE) return NULL;
+ (VOID) strcpy(line, " -- ------");
+ cp1 = line + 10;
+ while(cp1 < line + 4 + NBUFN + 1) *cp1++ = ' ';
+ (VOID) strcpy(cp1, "---- ----");
+ if (addline(blp, line) == FALSE) return NULL;
+ bp = bheadp; /* For all buffers */
+ while (bp != NULL) {
+ cp1 = &line[0]; /* Start at left edge */
+ *cp1++ = (bp == curbp) ? '.' : ' ';
+ *cp1++ = ((bp->b_flag&BFCHG) != 0) ? '*' : ' ';
+ *cp1++ = ' '; /* Gap. */
+ *cp1++ = ' ';
+ cp2 = &bp->b_bname[0]; /* Buffer name */
+ while ((c = *cp2++) != 0)
+ *cp1++ = c;
+ while (cp1 < &line[4+NBUFN+1])
+ *cp1++ = ' ';
+ nbytes = 0; /* Count bytes in buf. */
+ if (bp != blp) {
+ lp = lforw(bp->b_linep);
+ while (lp != bp->b_linep) {
+ nbytes += llength(lp)+1;
+ lp = lforw(lp);
+ }
+ if(nbytes) nbytes--; /* no bonus newline */
+ }
+ (VOID) itor(b, 6, nbytes); /* 6 digit buffer size. */
+ cp2 = &b[0];
+ while ((c = *cp2++) != 0)
+ *cp1++ = c;
+ *cp1++ = ' '; /* Gap.. */
+ cp2 = &bp->b_fname[0]; /* File name */
+ if (*cp2 != 0) {
+ while ((c = *cp2++) != 0) {
+ if (cp1 < &line[128-1])
+ *cp1++ = c;
+ }
+ }
+ *cp1 = 0; /* Add to the buffer. */
+ if (addline(blp, line) == FALSE)
+ return NULL;
+ bp = bp->b_bufp;
+ }
+ blp->b_dotp = lforw(blp->b_linep); /* put dot at beginning of buffer */
+ blp->b_doto = 0;
+ return blp; /* All done */
+}
+
+/*
+ * Used above.
+ */
+static RSIZE itor(buf, width, num)
+register char buf[]; register int width; register RSIZE num; {
+ register RSIZE r;
+
+ if (num / 10 == 0) {
+ buf[0] = (num % 10) + '0';
+ for (r = 1; r < width; buf[r++] = ' ')
+ ;
+ buf[width] = '\0';
+ return 1;
+ } else {
+ buf[r = itor(buf, width, num / (RSIZE)10)] =
+ (num % (RSIZE)10) + '0';
+ return r + 1;
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * The argument "text" points to
+ * a 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.
+ */
+addline(bp, text) register BUFFER *bp; char *text; {
+ register LINE *lp;
+ register int i;
+ register int ntext;
+
+ ntext = strlen(text);
+ if ((lp=lalloc(ntext)) == NULL)
+ return FALSE;
+ for (i=0; i<ntext; ++i)
+ lputc(lp, i, text[i]);
+ bp->b_linep->l_bp->l_fp = lp; /* Hook onto the end */
+ lp->l_bp = bp->b_linep->l_bp;
+ bp->b_linep->l_bp = lp;
+ lp->l_fp = bp->b_linep;
+#ifdef CANTHAPPEN
+ if (bp->b_dotp == bp->b_linep) /* If "." is at the end */
+ bp->b_dotp = lp; /* move it to new line */
+ if (bp->b_markp == bp->b_linep) /* ditto for mark */
+ bp->b_markp = lp;
+#endif
+ 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.
+ */
+anycb(f) {
+ register BUFFER *bp;
+ register int s = FALSE, save = FALSE;
+ char prompt[NFILEN + 11];
+ VOID upmodes();
+
+ for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+ if (*(bp->b_fname) != '\0'
+ && (bp->b_flag&BFCHG) != 0) {
+ (VOID) strcpy(prompt, "Save file ");
+ (VOID) strcpy(prompt + 10, bp->b_fname);
+ if ((f == TRUE || (save = eyorn(prompt)) == TRUE)
+ && buffsave(bp) == TRUE) {
+ bp->b_flag &= ~BFCHG;
+ upmodes(bp);
+ } else 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 buffer and put it in the list of
+ * all buffers. Return pointer to the BUFFER
+ * block for the buffer.
+ */
+BUFFER *
+bfind(bname, cflag) register char *bname; {
+ register BUFFER *bp;
+ char *malloc();
+ register LINE *lp;
+ int i;
+ extern int defb_nmodes;
+ extern MAPS *defb_modes[PBMODES];
+ extern int defb_flag;
+
+ bp = bheadp;
+ while (bp != NULL) {
+ if (fncmp(bname, bp->b_bname) == 0)
+ return bp;
+ bp = bp->b_bufp;
+ }
+ if (cflag!=TRUE) return NULL;
+ /*NOSTRICT*/
+ if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL) {
+ ewprintf("Can't get %d bytes", sizeof(BUFFER));
+ return NULL;
+ }
+ if ((bp->b_bname=malloc((unsigned)(strlen(bname)+1))) == NULL) {
+ ewprintf("Can't get %d bytes", strlen(bname)+1);
+ free((char *) bp);
+ return NULL;
+ }
+ if ((lp = lalloc(0)) == NULL) {
+ free(bp->b_bname);
+ free((char *) 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;
+ bp->b_nwnd = 0;
+ bp->b_linep = lp;
+ bp->b_nmodes = defb_nmodes;
+ i = 0;
+ do {
+ bp->b_modes[i] = defb_modes[i];
+ } while(i++ < defb_nmodes);
+ bp->b_fname[0] = '\0';
+ bzero(&bp->b_fi, sizeof(bp->b_fi));
+ (VOID) strcpy(bp->b_bname, bname);
+ lp->l_fp = lp;
+ lp->l_bp = lp;
+ bp->b_bufp = bheadp;
+ bheadp = bp;
+ 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.
+ */
+bclear(bp) register BUFFER *bp; {
+ register LINE *lp;
+ register int s;
+ VOID lfree();
+
+ if ((bp->b_flag&BFCHG) != 0 /* Changed. */
+ && (s=eyesno("Buffer modified; kill anyway")) != TRUE)
+ return (s);
+ bp->b_flag &= ~BFCHG; /* Not changed */
+ while ((lp=lforw(bp->b_linep)) != bp->b_linep)
+ lfree(lp);
+ bp->b_dotp = bp->b_linep; /* Fix "." */
+ bp->b_doto = 0;
+ bp->b_markp = NULL; /* Invalidate "mark" */
+ bp->b_marko = 0;
+ return TRUE;
+}
+
+/*
+ * Display the given buffer in the given window. Flags indicated
+ * action on redisplay.
+ */
+showbuffer(bp, wp, flags) register BUFFER *bp; register WINDOW *wp; {
+ register BUFFER *obp;
+ WINDOW *owp;
+
+ if (wp->w_bufp == bp) { /* Easy case! */
+ wp->w_flag |= flags;
+ return TRUE;
+ }
+
+ /* First, dettach 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;
+ }
+ }
+
+ /* 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;
+ } 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;
+ break;
+ }
+ wp->w_flag |= WFMODE|flags;
+ return TRUE;
+}
+
+/*
+ * Pop the buffer we got passed onto the screen.
+ * Returns a status.
+ */
+WINDOW *
+popbuf(bp) register BUFFER *bp; {
+ register WINDOW *wp;
+
+ if (bp->b_nwnd == 0) { /* Not on screen yet. */
+ if ((wp=wpopup()) == NULL) return NULL;
+ } else
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
+ if (wp->w_bufp == bp) {
+ wp->w_flag |= WFHARD|WFFORCE;
+ return wp ;
+ }
+ if (showbuffer(bp, wp, WFHARD) != TRUE) return NULL;
+ return wp;
+}
+
+/*
+ * Insert another buffer at dot. Very useful.
+ */
+/*ARGSUSED*/
+bufferinsert(f, n)
+{
+ register BUFFER *bp;
+ register LINE *clp;
+ register int clo;
+ register int nline;
+ int s;
+ char bufn[NBUFN];
+
+ /* Get buffer to use from user */
+ if (curbp->b_altb != NULL)
+ s=eread("Insert buffer: (default %s) ", bufn, NBUFN,
+ EFNEW|EFBUF, &(curbp->b_altb->b_bname),
+ (char *) NULL) ;
+ else
+ s=eread("Insert buffer: ", bufn, NBUFN, EFNEW|EFBUF,
+ (char *) NULL) ;
+ if (s == ABORT) return (s);
+ if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb;
+ else if ((bp=bfind(bufn, FALSE)) == NULL) return FALSE;
+
+ if (bp==curbp) {
+ ewprintf("Cannot insert buffer into self");
+ return FALSE;
+ }
+
+ /* insert the buffer */
+ nline = 0;
+ clp = lforw(bp->b_linep);
+ for(;;) {
+ for (clo = 0; clo < llength(clp); clo++)
+ if (linsert(1, lgetc(clp, clo)) == FALSE)
+ return FALSE;
+ if((clp = lforw(clp)) == bp->b_linep) break;
+ if (newline(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_linep)
+ clp = lback(clp);
+ curwp->w_linep = clp; /* adjust framing. */
+ curwp->w_flag |= WFHARD;
+ }
+ return (TRUE);
+}
+
+/*
+ * Turn off the dirty bit on this buffer.
+ */
+/*ARGSUSED*/
+notmodified(f, n)
+{
+ register WINDOW *wp;
+
+ curbp->b_flag &= ~BFCHG;
+ wp = wheadp; /* Update mode lines. */
+ while (wp != NULL) {
+ if (wp->w_bufp == curbp)
+ wp->w_flag |= WFMODE;
+ wp = wp->w_wndp;
+ }
+ ewprintf("Modification-flag cleared");
+ return TRUE;
+}
+
+#ifndef NO_HELP
+/*
+ * Popbuf and set all windows to top of buffer. Currently only used by
+ * help functions.
+ */
+
+popbuftop(bp)
+register BUFFER *bp;
+{
+ register WINDOW *wp;
+
+ bp->b_dotp = lforw(bp->b_linep);
+ 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_flag |= WFHARD;
+ }
+ }
+ return popbuf(bp) != NULL;
+}
+#endif
diff --git a/usr.bin/mg/chrdef.h b/usr.bin/mg/chrdef.h
new file mode 100644
index 00000000000..6c2f2f3180f
--- /dev/null
+++ b/usr.bin/mg/chrdef.h
@@ -0,0 +1,79 @@
+/*
+ * sys/default/chardef.h: character set specific #defines for mg 2a
+ * Warning: System specific ones exist
+ */
+
+#ifndef CHARMASK
+/*
+ * casting should be at least as efficent as anding with 0xff,
+ * and won't have the size problems. Override in sysdef.h if no
+ * unsigned char type.
+ */
+#define CHARMASK(c) ((unsigned char) (c))
+#endif
+
+/*
+ * 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 _W 0x01 /* Word. */
+#define _U 0x02 /* Upper case letter. */
+#define _L 0x04 /* Lower case letter. */
+#define _C 0x08 /* Control. */
+#define _P 0x10 /* end of sentence punctuation */
+#define _D 0x20 /* is decimal digit */
+
+#define ISWORD(c) ((cinfo[CHARMASK(c)]&_W)!=0)
+#define ISCTRL(c) ((cinfo[CHARMASK(c)]&_C)!=0)
+#define ISUPPER(c) ((cinfo[CHARMASK(c)]&_U)!=0)
+#define ISLOWER(c) ((cinfo[CHARMASK(c)]&_L)!=0)
+#define ISEOSP(c) ((cinfo[CHARMASK(c)]&_P)!=0)
+#define ISDIGIT(c) ((cinfo[CHARMASK(c)]&_D)!=0)
+#define TOUPPER(c) ((c)-0x20)
+#define TOLOWER(c) ((c)+0x20)
+
+/*
+ * generally useful thing for chars
+ */
+#define CCHR(x) ((x) ^ 0x40) /* CCHR('?') == DEL */
+
+#ifndef METACH
+#define METACH CCHR('[')
+#endif
+
+#ifdef XKEYS
+#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
+#endif
diff --git a/usr.bin/mg/cinfo.c b/usr.bin/mg/cinfo.c
new file mode 100644
index 00000000000..163017e0e07
--- /dev/null
+++ b/usr.bin/mg/cinfo.c
@@ -0,0 +1,135 @@
+/*
+ * 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 "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.
+ */
+char cinfo[256] = {
+ _C, _C, _C, _C, /* 0x0X */
+ _C, _C, _C, _C,
+ _C, _C, _C, _C,
+ _C, _C, _C, _C,
+ _C, _C, _C, _C, /* 0x1X */
+ _C, _C, _C, _C,
+ _C, _C, _C, _C,
+ _C, _C, _C, _C,
+ 0, _P, 0, 0, /* 0x2X */
+ _W, _W, 0, _W,
+ 0, 0, 0, 0,
+ 0, 0, _P, 0,
+ _D|_W, _D|_W, _D|_W, _D|_W, /* 0x3X */
+ _D|_W, _D|_W, _D|_W, _D|_W,
+ _D|_W, _D|_W, 0, 0,
+ 0, 0, 0, _P,
+ 0, _U|_W, _U|_W, _U|_W, /* 0x4X */
+ _U|_W, _U|_W, _U|_W, _U|_W,
+ _U|_W, _U|_W, _U|_W, _U|_W,
+ _U|_W, _U|_W, _U|_W, _U|_W,
+ _U|_W, _U|_W, _U|_W, _U|_W, /* 0x5X */
+ _U|_W, _U|_W, _U|_W, _U|_W,
+ _U|_W, _U|_W, _U|_W, 0,
+ 0, 0, 0, 0,
+ 0, _L|_W, _L|_W, _L|_W, /* 0x6X */
+ _L|_W, _L|_W, _L|_W, _L|_W,
+ _L|_W, _L|_W, _L|_W, _L|_W,
+ _L|_W, _L|_W, _L|_W, _L|_W,
+ _L|_W, _L|_W, _L|_W, _L|_W, /* 0x7X */
+ _L|_W, _L|_W, _L|_W, _L|_W,
+ _L|_W, _L|_W, _L|_W, 0,
+ 0, 0, 0, _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,
+ _U|_W, _U|_W, _U|_W, _U|_W, /* 0xCX */
+ _U|_W, _U|_W, _U|_W, _U|_W,
+ _U|_W, _U|_W, _U|_W, _U|_W,
+ _U|_W, _U|_W, _U|_W, _U|_W,
+ 0, _U|_W, _U|_W, _U|_W, /* 0xDX */
+ _U|_W, _U|_W, _U|_W, _U|_W,
+ _U|_W, _U|_W, _U|_W, _U|_W,
+ _U|_W, _U|_W, 0, _W,
+ _L|_W, _L|_W, _L|_W, _L|_W, /* 0xEX */
+ _L|_W, _L|_W, _L|_W, _L|_W,
+ _L|_W, _L|_W, _L|_W, _L|_W,
+ _L|_W, _L|_W, _L|_W, _L|_W,
+ 0, _L|_W, _L|_W, _L|_W, /* 0xFX */
+ _L|_W, _L|_W, _L|_W, _L|_W,
+ _L|_W, _L|_W, _L|_W, _L|_W,
+ _L|_W, _L|_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'.
+ */
+
+char *keyname(cp, k)
+register char *cp;
+register int k;
+{
+ register char *np;
+#ifdef FKEYS
+ extern char *keystrings[];
+#endif
+
+ if(k < 0) k = CHARMASK(k); /* sign extended char */
+ switch(k) {
+ case CCHR('@'): np = "NUL"; break;
+ case CCHR('I'): np = "TAB"; break;
+ case CCHR('J'): np = "LFD"; break; /* yuck, but that's what GNU calls it */
+ case CCHR('M'): np = "RET"; break;
+ case CCHR('['): np = "ESC"; break;
+ case ' ': np = "SPC"; break; /* yuck again */
+ case CCHR('?'): np = "DEL"; break;
+ default:
+#ifdef FKEYS
+ if(k >= KFIRST && k <= KLAST &&
+ (np = keystrings[k - KFIRST]) != NULL)
+ break;
+#endif
+ if(k > CCHR('?')) {
+ *cp++ = '0';
+ *cp++ = ((k>>6)&7) + '0';
+ *cp++ = ((k>>3)&7) + '0';
+ *cp++ = (k&7) + '0';
+ *cp = '\0';
+ return cp;
+ }
+ if(k < ' ') {
+ *cp++ = 'C';
+ *cp++ = '-';
+ k = CCHR(k);
+ if(ISUPPER(k)) k = TOLOWER(k);
+ }
+ *cp++ = k;
+ *cp = '\0';
+ return cp;
+ }
+ (VOID) strcpy(cp, np);
+ return cp + strlen(cp);
+}
diff --git a/usr.bin/mg/def.h b/usr.bin/mg/def.h
new file mode 100644
index 00000000000..a54956cab83
--- /dev/null
+++ b/usr.bin/mg/def.h
@@ -0,0 +1,312 @@
+/*
+ * This file is the general header file for all parts
+ * of the MicroEMACS 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.
+ * The most common reason to edit this file would be to zap
+ * the definition of CVMVAS or BACKUP.
+ */
+#include "sysdef.h" /* Order is critical. */
+#include "ttydef.h"
+#include "chrdef.h"
+
+/*
+ * If your system and/or compiler does not support the "void" type
+ * then define NO_VOID_TYPE in sysdef.h. In the absence of some
+ * other definition for VOID, the default in that case will be to
+ * turn it into an int, which works with most compilers that don't
+ * support void. In the absence of any definition of VOID or
+ * NO_VOID_TYPE, the default is to assume void is supported, which
+ * should be the case for most modern C compilers.
+ */
+
+#ifdef NO_VOID_TYPE
+# undef VOID
+# define VOID int /* Default for no void is int */
+#else
+#ifndef VOID
+# define VOID void /* Just use normal void */
+#endif /* VOID */
+#endif /* NO_VOID_TYPE */
+
+#ifdef NO_MACRO
+#ifndef NO_STARTUP
+#define NO_STARTUP /* NO_MACRO implies NO_STARTUP */
+#endif
+#endif
+
+typedef int (*PF)(); /* generaly useful type */
+
+/*
+ * Table sizes, etc.
+ */
+#define NFILEN 80 /* Length, file name. */
+#define NBUFN 24 /* Length, buffer name. */
+#define NLINE 256 /* Length, line. */
+#define PBMODES 4 /* modes per buffer */
+#define NKBDM 256 /* Length, keyboard macro. */
+#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 */
+/*
+ * Universal.
+ */
+#define FALSE 0 /* False, no, bad, etc. */
+#define TRUE 1 /* True, yes, good, etc. */
+#define ABORT 2 /* Death, ^G, abort, etc. */
+
+#define KPROMPT 2 /* keyboard prompt */
+
+/*
+ * 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, 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 */
+
+/*
+ * Directory I/O.
+ */
+#define DIOSUC 0 /* Success. */
+#define DIOEOF 1 /* End of file. */
+#define DIOERR 2 /* Error. */
+
+/*
+ * Display colors.
+ */
+#define CNONE 0 /* Unknown color. */
+#define CTEXT 1 /* Text color. */
+#define CMODE 2 /* Mode line color. */
+
+/* Flags for keyboard involked functions */
+
+#define FFUNIV 1 /* universal argument */
+#define FFNEGARG 2 /* negitive 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 autocompleteion on */
+#define EFNEW 0x0008 /* New prompt. */
+#define EFCR 0x0010 /* Echo CR at end; last read. */
+
+/*
+ * Flags for "ldelete"/"kinsert"
+ */
+
+#define KNONE 0
+#define KFORW 1
+#define KBACK 2
+
+/*
+ * 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". Each line contains a 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.
+ */
+typedef 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 */
+#ifndef ZEROARRAY
+ char l_text[1]; /* A bunch of characters. */
+#else
+ char l_text[]; /* A bunch of characters. */
+#endif
+} LINE;
+
+/*
+ * The rationale behind these macros is that you
+ * could (with some editing, like changing the type of a line
+ * link from a "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.
+ */
+typedef struct LIST {
+ union {
+ struct WINDOW *l_wp;
+ struct BUFFER *x_bp; /* l_bp is used by LINE */
+ struct LIST *l_nxt;
+ } l_p;
+ char *l_name;
+} LIST;
+/*
+ * 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.
+ */
+typedef struct WINDOW {
+ 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" */
+ char w_toprow; /* Origin 0 top row of window */
+ char w_ntrows; /* # of rows of text in window */
+ char w_force; /* If NZ, forcing row. */
+ char w_flag; /* Flags. */
+} WINDOW;
+#define w_wndp w_list.l_p.l_wp
+#define w_name w_list.l_name
+
+/*
+ * Window flags are set by command processors to
+ * tell the display system what has happened to the buffer
+ * mapped by the window. Setting "WFHARD" 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 WFFORCE 0x01 /* Force reframe. */
+#define WFMOVE 0x02 /* Movement from line to line. */
+#define WFEDIT 0x04 /* Editing within a line. */
+#define WFHARD 0x08 /* Better to a full display. */
+#define WFMODE 0x10 /* Update mode line. */
+
+/*
+ * 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_linep".
+ */
+typedef struct BUFFER {
+ 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_linep; /* 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 */
+ struct fileinfo b_fi; /* File attributes */
+} BUFFER;
+#define b_bufp b_list.l_p.x_bp
+#define b_bname b_list.l_name
+
+#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 */
+
+/*
+ * 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.
+ */
+typedef struct {
+ struct LINE *r_linep; /* Origin LINE address. */
+ int r_offset; /* Origin LINE offset. */
+ RSIZE r_size; /* Length in characters. */
+} REGION;
+
+/*
+ * Externals.
+ */
+extern int thisflag;
+extern int lastflag;
+extern int curgoal;
+extern int epresf;
+extern int sgarbf;
+extern int mode;
+extern WINDOW *curwp;
+extern BUFFER *curbp;
+extern WINDOW *wheadp;
+extern BUFFER *bheadp;
+extern char pat[];
+extern BUFFER *bfind();
+extern WINDOW *popbuf();
+extern WINDOW *wpopup();
+extern LINE *lalloc();
+extern LINE *lallocx();
+extern VOID ewprintf();
+extern int nrow;
+extern int ncol;
+extern int ttrow;
+extern int ttcol;
+extern int tceeol;
+extern int tcinsl;
+extern int tcdell;
+extern char cinfo[];
+extern char *keystrings[];
+extern VOID update();
+extern char *keyname();
+extern char *adjustname();
+extern VOID kdelete();
+extern VOID lchange();
+/*
+ * Standard I/O.
+ */
+extern char *strcpy();
+extern char *strcat();
+extern char *malloc();
diff --git a/usr.bin/mg/dir.c b/usr.bin/mg/dir.c
new file mode 100644
index 00000000000..b8f40f973ee
--- /dev/null
+++ b/usr.bin/mg/dir.c
@@ -0,0 +1,59 @@
+/*
+ * Name: MG 2a
+ * Directory management functions
+ * Created: Ron Flax (ron@vsedev.vse.com)
+ * Modified for MG 2a by Mic Kaczmarczik 03-Aug-1987
+ */
+
+#include "def.h"
+
+#ifndef NO_DIR
+#ifndef getwd /* may be a #define */
+char *getwd();
+#endif
+char *wdir;
+static char cwd[NFILEN];
+
+/*
+ * Initialize anything the directory management routines need
+ */
+dirinit()
+{
+ if (!(wdir = getwd(cwd)))
+ panic("Can't get current directory!");
+}
+
+/*
+ * Change current working directory
+ */
+/*ARGSUSED*/
+changedir(f, n)
+{
+ register int s;
+ char bufc[NPAT];
+
+ if ((s=ereply("Change default directory: ", bufc, NPAT)) != TRUE)
+ return(s);
+ if (bufc[0] == '\0')
+ (VOID) strcpy(bufc, wdir);
+ if (chdir(bufc) == -1) {
+ ewprintf("Can't change dir to %s", bufc);
+ return(FALSE);
+ } else {
+ if (!(wdir = getwd(cwd)))
+ panic("Can't get current directory!");
+ ewprintf("Current directory is now %s", wdir);
+ return(TRUE);
+ }
+}
+
+/*
+ * Show current directory
+ */
+/*ARGSUSED*/
+showcwdir(f, n)
+{
+ ewprintf("Current directory: %s", wdir);
+ return(TRUE);
+}
+#endif
diff --git a/usr.bin/mg/dired.c b/usr.bin/mg/dired.c
new file mode 100644
index 00000000000..2d3aedbbb4e
--- /dev/null
+++ b/usr.bin/mg/dired.c
@@ -0,0 +1,195 @@
+/* dired module for mg 2a */
+/* by Robert A. Larson */
+
+#include "def.h"
+
+#ifndef NO_DIRED
+
+BUFFER *dired_();
+
+/*ARGSUSED*/
+dired(f, n)
+int f, n;
+{
+ char dirname[NFILEN];
+ BUFFER *bp;
+
+ dirname[0] = '\0';
+ if(eread("Dired: ", dirname, NFILEN, EFNEW | EFCR) == ABORT)
+ return ABORT;
+ if((bp = dired_(dirname)) == NULL) return FALSE;
+ curbp = bp;
+ return showbuffer(bp, curwp, WFHARD | WFMODE);
+}
+
+/*ARGSUSED*/
+d_otherwindow(f, n)
+int f, n;
+{
+ char dirname[NFILEN];
+ BUFFER *bp;
+ WINDOW *wp;
+
+ dirname[0] = '\0';
+ if(eread("Dired other window: ", dirname, NFILEN, EFNEW | EFCR) == ABORT)
+ return ABORT;
+ if((bp = dired_(dirname)) == NULL) return FALSE;
+ if((wp = popbuf(bp)) == NULL) return FALSE;
+ curbp = bp;
+ curwp = wp;
+ return TRUE;
+}
+
+/*ARGSUSED*/
+d_del(f, n)
+int f, n;
+{
+ if(n < 0) return FALSE;
+ while(n--) {
+ if(llength(curwp->w_dotp) > 0)
+ lputc(curwp->w_dotp, 0, 'D');
+ if(lforw(curwp->w_dotp) != curbp->b_linep)
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ }
+ curwp->w_flag |= WFEDIT | WFMOVE;
+ curwp->w_doto = 0;
+ return TRUE;
+}
+
+/*ARGSUSED*/
+d_undel(f, n)
+int f, 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_linep)
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ }
+ curwp->w_flag |= WFEDIT | WFMOVE;
+ curwp->w_doto = 0;
+ return TRUE;
+}
+
+/*ARGSUSED*/
+d_undelbak(f, n)
+int f, n;
+{
+ if(n < 0) return d_undel(f, -n);
+ while(n--) {
+ if(llength(curwp->w_dotp) > 0)
+ lputc(curwp->w_dotp, 0, ' ');
+ if(lback(curwp->w_dotp) != curbp->b_linep)
+ curwp->w_dotp = lback(curwp->w_dotp);
+ }
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFEDIT | WFMOVE;
+ return TRUE;
+}
+
+/*ARGSUSED*/
+d_findfile(f, n)
+int f, n;
+{
+ char fname[NFILEN];
+ register BUFFER *bp;
+ register int s;
+ BUFFER *findbuffer();
+
+ if((s = d_makename(curwp->w_dotp, fname)) == ABORT) return FALSE;
+ if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL) return FALSE;
+ curbp = bp;
+ if (showbuffer(bp, curwp, WFHARD) != TRUE) return FALSE;
+ if (bp->b_fname[0] != 0) return TRUE;
+ return readin(fname);
+}
+
+/*ARGSUSED*/
+d_ffotherwindow(f, n)
+int f, n;
+{
+ char fname[NFILEN];
+ register BUFFER *bp;
+ register int s;
+ register WINDOW *wp;
+ BUFFER *findbuffer();
+
+ if((s = d_makename(curwp->w_dotp, fname)) == ABORT) return FALSE;
+ if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL) return FALSE;
+ if ((wp = popbuf(bp)) == NULL) return FALSE;
+ curbp = bp;
+ curwp = wp;
+ if (bp->b_fname[0] != 0) return TRUE; /* never true for dired buffers */
+ return readin(fname);
+}
+
+/*ARGSUSED*/
+d_expunge(f, n)
+int f, n;
+{
+ register LINE *lp, *nlp;
+ char fname[NFILEN];
+ VOID lfree();
+
+ for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = nlp) {
+ nlp = lforw(lp);
+ if(llength(lp) && lgetc(lp, 0) == 'D') {
+ switch(d_makename(lp, fname)) {
+ case ABORT:
+ ewprintf("Bad line in dired buffer");
+ return FALSE;
+ case FALSE:
+ if(unlink(fname) < 0) {
+ ewprintf("Could not delete '%s'", fname);
+ return FALSE;
+ }
+ break;
+ case TRUE:
+ if(unlinkdir(fname) < 0) {
+ ewprintf("Could not delete directory '%s'", fname);
+ return FALSE;
+ }
+ break;
+ }
+ lfree(lp);
+ curwp->w_flag |= WFHARD;
+ }
+ }
+ return TRUE;
+}
+
+/*ARGSUSED*/
+d_copy(f, n)
+int f, n;
+{
+ char frname[NFILEN], toname[NFILEN];
+ int stat;
+
+ if(d_makename(curwp->w_dotp, frname) != FALSE) {
+ ewprintf("Not a file");
+ return FALSE;
+ }
+ if((stat = eread("Copy %s to: ", toname, NFILEN, EFNEW | EFCR, frname))
+ != TRUE) return stat;
+ return copy(frname, toname) >= 0;
+}
+
+/*ARGSUSED*/
+d_rename(f, n)
+int f, n;
+{
+ char frname[NFILEN], toname[NFILEN];
+ int stat;
+
+ if(d_makename(curwp->w_dotp, frname) != FALSE) {
+ ewprintf("Not a file");
+ return FALSE;
+ }
+ if((stat = eread("Rename %s to: ", toname, NFILEN, EFNEW | EFCR, frname))
+ != TRUE) return stat;
+ return Xrename(frname, toname) >= 0;
+}
+#endif
+
+
diff --git a/usr.bin/mg/display.c b/usr.bin/mg/display.c
new file mode 100644
index 00000000000..40fd1935a19
--- /dev/null
+++ b/usr.bin/mg/display.c
@@ -0,0 +1,884 @@
+/*
+ * 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. Two conditional compilation flags; the GOSLING
+ * flag enables dynamic programming redisplay, using the
+ * algorithm published by Jim Gosling in SIGOA. The MEMMAP
+ * changes things around for memory mapped video. With
+ * both off, the terminal is a VT52.
+ */
+#include "def.h"
+#include "kbd.h"
+
+/*
+ * You can change these back to the types
+ * implied by the name if you get tight for space. If you
+ * make both of them "int" you get better code on the VAX.
+ * They do nothing if this is not Gosling redisplay, except
+ * for change the size of a structure that isn't used.
+ * A bit of a cheat.
+ */
+/* These defines really belong in sysdef.h */
+#ifndef XCHAR
+# define XCHAR int
+# define XSHORT int
+#endif
+
+#ifdef STANDOUT_GLITCH
+extern int SG; /* number of standout glitches */
+#endif
+
+/*
+ * A video structure always holds
+ * an array of characters whose length is equal to
+ * the longest line possible. Only some of this is
+ * used if "ncol" isn't the same as "NCOL".
+ */
+typedef struct {
+ short v_hash; /* Hash code, for compares. */
+ short v_flag; /* Flag word. */
+ short v_color; /* Color of the line. */
+ XSHORT v_cost; /* Cost of display. */
+ char v_text[NCOL]; /* The actual characters. */
+} VIDEO;
+
+#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.
+ * If no fancy redisplay, this isn't used. The trace index
+ * fields can be "char", and the score a "short", but
+ * this makes the code worse on the VAX.
+ */
+typedef struct {
+ XCHAR s_itrace; /* "i" index for track back. */
+ XCHAR s_jtrace; /* "j" index for trace back. */
+ XSHORT s_cost; /* Display cost. */
+} SCORE;
+
+int sgarbf = TRUE; /* TRUE if screen is garbage. */
+int vtrow = 0; /* Virtual cursor row. */
+int vtcol = 0; /* 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 */
+
+VIDEO *vscreen[NROW-1]; /* Edge vector, virtual. */
+VIDEO *pscreen[NROW-1]; /* Edge vector, physical. */
+VIDEO video[2*(NROW-1)]; /* Actual screen data. */
+VIDEO blanks; /* Blank line image. */
+
+/*
+ * Some predeclerations to make ANSI compilers happy
+ */
+VOID vtinit();
+VOID vttidy();
+VOID vtmove();
+VOID vtputc();
+VOID vtpute();
+VOID vteeol();
+VOID update();
+VOID updext();
+VOID ucopy();
+VOID uline();
+VOID modeline();
+VOID hash();
+VOID setscores();
+VOID traceback();
+
+#ifdef GOSLING
+/*
+ * 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.
+ */
+SCORE score[NROW*NROW];
+#endif
+
+/*
+ * 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() {
+ register VIDEO *vp;
+ register int i;
+
+ ttopen();
+ ttinit();
+ vp = &video[0];
+ for (i=0; i<NROW-1; ++i) {
+ vscreen[i] = vp;
+ ++vp;
+ pscreen[i] = vp;
+ ++vp;
+ }
+ 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() {
+ 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(row, 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(c) register int c; {
+ register VIDEO *vp;
+
+ 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
+ vp->v_text[vtcol++] = c;
+}
+
+/* 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(c)
+int c;
+{
+ register VIDEO *vp;
+
+ 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 (vtcol >= 0) vp->v_text[vtcol] = c;
+ ++vtcol;
+ }
+}
+
+/* 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() {
+ register 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() {
+ register LINE *lp;
+ register WINDOW *wp;
+ register VIDEO *vp1;
+ VIDEO *vp2;
+ register int i;
+ register int j;
+ register int c;
+ register int hflag;
+ register int currow;
+ register int curcol;
+ register int offs;
+ register int size;
+ VOID traceback ();
+ VOID uline ();
+
+ if (typeahead()) return;
+ if (sgarbf) { /* must update everything */
+ wp = wheadp;
+ while(wp != NULL) {
+ wp->w_flag |= WFMODE | WFHARD;
+ wp = wp->w_wndp;
+ }
+ }
+ hflag = FALSE; /* Not hard. */
+ wp = wheadp;
+ while (wp != NULL) {
+ if (wp->w_flag != 0) { /* Need update. */
+ if ((wp->w_flag&WFFORCE) == 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_linep)
+ break;
+ lp = lforw(lp);
+ }
+ }
+ i = wp->w_force; /* Reframe this one. */
+ 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;
+ lp = wp->w_dotp;
+ while (i!=0 && lback(lp)!=wp->w_bufp->b_linep) {
+ --i;
+ lp = lback(lp);
+ }
+ wp->w_linep = lp;
+ wp->w_flag |= WFHARD; /* Force full. */
+ out:
+ lp = wp->w_linep; /* Try reduced update. */
+ i = wp->w_toprow;
+ if ((wp->w_flag&~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_flag&(WFEDIT|WFHARD)) != 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_linep) {
+ for (j=0; j<llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+ lp = lforw(lp);
+ }
+ vteeol();
+ ++i;
+ }
+ }
+ if ((wp->w_flag&WFMODE) != 0)
+ modeline(wp);
+ wp->w_flag = 0;
+ wp->w_force = 0;
+ }
+ wp = wp->w_wndp;
+ }
+ 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;
+ else if (ISCTRL(c) != FALSE)
+ ++curcol;
+ ++curcol;
+ }
+ 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;
+ }
+#ifdef GOSLING
+ 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;
+ }
+#endif
+ 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(vvp, pvp) register VIDEO *vvp; register 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(currow, curcol)
+int currow, curcol;
+{
+ register LINE *lp; /* pointer to current line */
+ register int j; /* index into line */
+
+ /* 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(row, vvp, pvp) VIDEO *vvp; VIDEO *pvp; {
+#ifdef MEMMAP
+ putline(row+1, 1, &vvp->v_text[0]);
+#else
+ register char *cp1;
+ register char *cp2;
+ register char *cp3;
+ char *cp4;
+ char *cp5;
+ register 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 && SG >= 0) tteeol();
+#endif
+ ttcolor(vvp->v_color);
+#ifdef STANDOUT_GLITCH
+ cp1 = &vvp->v_text[SG > 0 ? SG : 0];
+ /* the odd code for SG==0 is to avoid putting the invisable
+ * glitch character on the next line.
+ * (Hazeltine executive 80 model 30)
+ */
+ cp2 = &vvp->v_text[ncol - (SG >= 0 ? (SG!=0 ? SG : 1) : 0)];
+#else
+ cp1 = &vvp->v_text[0];
+ cp2 = &vvp->v_text[ncol];
+#endif
+ while (cp1 != cp2) {
+ ttputc(*cp1++);
+ ++ttcol;
+ }
+#ifndef MOVE_STANDOUT
+ ttcolor(CTEXT);
+#endif
+ 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 && SG > 0) {
+ if(cp1 < &vvp->v_text[SG]) cp1 = &vvp->v_text[SG];
+ if(cp5 > &vvp->v_text[ncol-SG]) cp5 = &vvp->v_text[ncol-SG];
+ } else if (SG < 0)
+#endif
+ ttcolor(vvp->v_color);
+ while (cp1 != cp5) {
+ ttputc(*cp1++);
+ ++ttcol;
+ }
+ if (cp5 != cp3) /* Do erase. */
+ tteeol();
+#endif
+}
+
+/*
+ * Redisplay the mode line for
+ * the window pointed to by the "wp".
+ * This is the only routine that has any idea
+ * of how the modeline 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 SG characters
+ * may never be seen.
+ */
+VOID
+modeline(wp) register WINDOW *wp; {
+ register int n;
+ register BUFFER *bp;
+ int mode;
+
+ n = wp->w_toprow+wp->w_ntrows; /* Location. */
+ vscreen[n]->v_color = CMODE; /* 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&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(mode=0;;) {
+ n += vtputs(bp->b_modes[mode]->p_name);
+ if(++mode > bp->b_nmodes) break;
+ vtputc('-');
+ ++n;
+ }
+ vtputc(')');
+ ++n;
+ while (n < ncol) { /* Pad out. */
+ vtputc('-');
+ ++n;
+ }
+}
+/*
+ * output a string to the mode line, report how long it was.
+ */
+vtputs(s) register char *s; {
+ register int n = 0;
+
+ while (*s != '\0') {
+ vtputc(*s++);
+ ++n;
+ }
+ return n;
+}
+#ifdef GOSLING
+/*
+ * 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(vp) register VIDEO *vp; {
+ register int i;
+ register int n;
+ register 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(offs, size) {
+ register SCORE *sp;
+ SCORE *sp1;
+ register int tempcost;
+ register int bestcost;
+ register int j;
+ register int i;
+ register VIDEO **vp;
+ VIDEO **pp, **vbase, **pbase;
+
+ 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(offs, size, i, j) {
+ register int itrace;
+ register int jtrace;
+ register int k;
+ register int ninsl;
+ register int ndraw;
+ register int 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]);
+}
+#endif
diff --git a/usr.bin/mg/dvidoc.sty b/usr.bin/mg/dvidoc.sty
new file mode 100644
index 00000000000..9dbb9cb8937
--- /dev/null
+++ b/usr.bin/mg/dvidoc.sty
@@ -0,0 +1,181 @@
+% dvidoc style file. Fixes things up so your file uses only fixed width
+% non-math characters, at least if you don't use math mode
+%
+% The first section defines all the font change commands to use the
+% doc pseudo-font. It is taken from John Pavel's dvidoc.sty, March 1987
+%
+% version from John Pavel's dvidoc.sty, March 1987
+\def\rm{\protect\pdoc}
+\def\it{\protect\pdoc}
+\def\bf{\protect\pdoc}
+\def\sl{\protect\pdoc}
+\def\sf{\protect\pdoc}
+\def\sc{\protect\pdoc}
+\def\tt{\protect\pdoc}
+\newfam\docfam
+\def\pdoc{\@getfont\pdoc\docfam\@xpt{doc}}
+% This gets all normal text, headings, etc.
+% Unfortunately it doesn't catch places where more explicit stuff
+% is done. The following is brute force but effective. Note that
+% we leave the symbol fonts alone, since otherwise we'll get TeX
+% errors complaining that it couldn't find symbol fonts
+% Possibly we should do this for all of the font types. This caught
+% everything in sample.tex.
+\font\fivmi = doc
+\font\fivrm = doc
+\font\fivsy = cmsy10
+\font\sixmi = doc
+\font\sixrm = doc
+\font\sixsy = cmsy10
+\font\sevmi = doc
+\font\sevrm = doc
+\font\sevsy = cmsy10
+\font\egtmi = doc
+\font\egtrm = doc
+\font\egtsy = cmsy10
+\font\ninmi = doc
+\font\ninrm = doc
+\font\ninsy = cmsy10
+\font\tenmi = doc
+\font\tenrm = doc
+\font\elvmi = doc
+\font\elvrm = doc
+\font\elvsy = cmsy10
+\font\twlmi = doc
+\font\twlsy = cmsy10
+\font\frtnmi = doc
+\font\frtnrm = doc
+\font\frtnsy = cmsy10
+\font\svtnmi = doc
+\font\svtnrm = doc
+\font\svtnsy = cmsy10
+\font\twtymi = doc
+\font\twtyrm = doc
+\font\twtysy = cmsy10
+\rm
+%
+% These dimensional definitions are also taken from Langdon, though with
+% some changes, e.g. not ragged right/bottom and parind 3 instead of 5.
+% horizontal dimensions had best be multiples of \em
+\hsize 78 em % 78 characters per line so fit any screen
+% \rightskip=0pt plus 4em % ragged right
+% \spaceskip=1em % forces ONE space between words
+% \frenchspacing suppresses extra blanks after punctuation -don't
+\parindent=3em
+\def\enspace{\kern 1em} \def\enskip{\hskip 1em\relax}
+%
+% vertical skips may best be multiples of \baselineskip
+\baselineskip=12pt % 6 lines per inch
+% \vsize % default give 58 lines -OK
+\voffset=\baselineskip % so don't lose \headline
+\parskip=0pt
+\smallskipamount=0pt
+\medskipamount= \baselineskip
+\bigskipamount=2\baselineskip
+% \raggedbottom
+%
+% By default itemize is done with bullets, which we don't have. use hyphen
+% now fix up various latexish stuff
+%
+\def\labelitemi{ -}
+\def\labelitemii{ -}
+\def\labelitemiii{ -}
+\def\labelitemiv{ -}
+%
+% Fix up table of contents.
+%
+% Dottedtocline has to be redefined because
+% the original used math mode for the leader. Dots in math mode turn out
+% to be colons.
+%
+% We've also got problems with spacing. The spacing for section number
+% and page number isn't enough. Add 1em * level number in dottedtocline
+% for the section number. Unfortunately, chapter and part are hardcoded in
+% the style files and don't use dottedtocline. So we add 1em to
+% numberline. Note that section and below get both the numberline and
+% the dottedtocline adjustment.
+\def\@dottedtocline#1#2#3#4#5{\ifnum #1>\c@tocdepth \else
+ \vskip \z@ plus .2pt
+ {\hangindent #2\relax \rightskip \@tocrmarg \parfillskip -\rightskip
+ \parindent #2\relax\@afterindenttrue
+ \interlinepenalty\@M
+ \leavevmode
+ \@tempdima 1em
+ \multiply \@tempdima #1
+ \addtolength\@tempdima{#3}
+ #4\nobreak\leaders\hbox to 2em{\hss.\hss}\hfill \nobreak \hbox to\@pnumwidth{\hfil\rm #5}\par}\fi}
+\def\numberline#1{\addtolength\@tempdima{2em} \hbox to\@tempdima{#1\hfil}}
+% page number width and table of contents right margin
+\def\@pnumwidth{3em}
+\def\@tocrmarg {4em}
+%
+% dotfill also used math mode for the leaders.
+%
+\def\dotfill{\cleaders\hbox{\hss.\hss}\hfill\kern\z@}
+%
+% Can't really do superscripts, so do footnotes with []
+%
+\def\@makefnmark{\hbox{/\@thefnmark/}}
+\long\def\@makefntext#1{\parindent 1em\noindent
+ \hbox to 3em{\hss\@thefnmark.}\ #1}
+\skip\footins 24pt plus 4pt minus 2pt
+\def\footnoterule{\kern-12\p@
+\hbox to .4\columnwidth{\leaders\hbox{-}\hfill}}
+%
+% \arrayrulewidth 1em \doublerulesep 1em
+%
+% Some fairly obvious hacks. No odd/even pages in doc files. Can't do the
+% fancy TeX symbols.
+%
+\oddsidemargin 0pt \evensidemargin 0pt
+\def\TeX{TeX}
+\def\LaTeX{LaTeX}
+\def\SliTeX{SliTeX}
+\def\BibTeX{BibTeX}
+\def\copyright{(C)}
+%
+% special versions of stuff from xxx10.sty, since only one font size
+%
+\def\@normalsize{\@setsize\normalsize{12pt}\xpt\@xpt
+\abovedisplayskip 12pt
+\belowdisplayskip \abovedisplayskip
+\abovedisplayshortskip \z@
+\belowdisplayshortskip \z@
+\let\@listi\@listI} % Setting of \@listi added 9 Jun 87
+\let\small\@normalsize
+\let\footnotesize\@normalsize
+\normalsize
+\footnotesep 12pt
+\labelsep 10pt
+\def\@listI{\leftmargin\leftmargini \parsep 0pt%
+\topsep 12pt%
+\itemsep 12pt}
+\let\@listi\@listI
+\let\@listii\@listI
+\let\@listiii\@listI
+\let\@listiv\@listI
+\let\@listv\@listI
+\let\@listvi\@listI
+\@listI
+%
+% We had sort of random numbers of blank lines around section numbers.
+% Turns out they used various fractional spacing. Rather than depend
+% upon the definition of startsection, just wrap something around it
+% that normalizes the arguments to 12pt. Negative args have special
+% meanings. Alas, this trick doesn't fix up chapter and part entries,
+% as they have hardcoded extra vertical space. No way to do that
+% without special definitions for l@chapter, which would be different
+% for different styles. We're gonna get extra lines. Sorry.
+\let\@oldstartsec\@startsection
+\def\@startsection#1#2#3#4#5#6{
+ \@tempskipa #4\relax
+ \@tempskipb #5\relax
+ \ifdim \@tempskipa <\z@ \@tempskipa -12pt \else \@tempskipa 12pt \fi
+ \ifdim \@tempskipb >\z@ \@tempskipb 12pt \fi
+\@oldstartsec{#1}{#2}{#3}{\@tempskipa}{\@tempskipb}{#6}
+}
+%
+% Remaining unsolved problem: hrule and vrule are no-ops. This is
+% visible mostly in the tabular stuff, where none of the lines show up,
+% though it's still basically usable.
+%
diff --git a/usr.bin/mg/echo.c b/usr.bin/mg/echo.c
new file mode 100644
index 00000000000..3663cda638c
--- /dev/null
+++ b/usr.bin/mg/echo.c
@@ -0,0 +1,781 @@
+/*
+ * 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.
+ */
+/*
+ * The varargs lint directive comments are 0 an attempt to get lint to shup
+ * up about CORRECT usage of varargs.h. It won't.
+ */
+#include "def.h"
+#include "key.h"
+#ifdef LOCAL_VARARGS
+#include "varargs.h"
+#else
+#include <varargs.h>
+#endif
+#ifndef NO_MACRO
+# include "macro.h"
+#endif
+
+static int veread();
+VOID ewprintf();
+static VOID eformat();
+static VOID eputi();
+static VOID eputl();
+static VOID eputs();
+static VOID eputc();
+static int complt();
+static int complt_list();
+LIST * make_file_list();
+LIST * copy_list();
+char * strrchr();
+extern LIST * complete_function_list();
+
+int epresf = FALSE; /* Stuff in echo line flag. */
+
+extern int tthue;
+
+/*
+ * Erase the echo line.
+ */
+VOID
+eerase() {
+ ttcolor(CTEXT);
+ ttmove(nrow-1, 0);
+ tteeol();
+ ttflush();
+ epresf = FALSE;
+}
+
+/*
+ * Ask "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.
+ */
+eyorn(sp) char *sp; {
+ register int s;
+
+#ifndef NO_MACRO
+ if(inmacro) return TRUE;
+#endif
+ ewprintf("%s? (y or n) ", sp);
+ for (;;) {
+ s = getkey(FALSE);
+ if (s == 'y' || s == 'Y') return TRUE;
+ if (s == 'n' || s == 'N') return FALSE;
+ if (s == CCHR('G')) return ctrlg(FFRAND, 1);
+ ewprintf("Please answer y or n. %s? (y or n) ", sp);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Like eyorn, but for more important question. User must type either all of
+ * "yes" or "no", and the trainling newline.
+ */
+eyesno(sp) char *sp; {
+ register int s;
+ char buf[64];
+
+#ifndef NO_MACRO
+ if(inmacro) return TRUE;
+#endif
+ s = ereply("%s? (yes or no) ", buf, sizeof(buf), sp);
+ for (;;) {
+ if (s == ABORT) return ABORT;
+ if (s != FALSE) {
+#ifndef NO_MACRO
+ if (macrodef) {
+ LINE *lp = maclcur;
+
+ maclcur = lp->l_bp;
+ maclcur->l_fp = lp->l_fp;
+ free((char *)lp);
+ }
+#endif
+ if ((buf[0] == 'y' || buf[0] == 'Y')
+ && (buf[1] == 'e' || buf[1] == 'E')
+ && (buf[2] == 's' || buf[2] == 'S')
+ && (buf[3] == '\0')) return TRUE;
+ if ((buf[0] == 'n' || buf[0] == 'N')
+ && (buf[1] == 'o' || buf[0] == 'O')
+ && (buf[2] == '\0')) return FALSE;
+ }
+ s = ereply("Please answer yes or no. %s? (yes or no) ",
+ buf, sizeof(buf), sp);
+ }
+ /*NOTREACHED*/
+}
+/*
+ * Write out a prompt, and read back a
+ * reply. The prompt is now written out with full "ewprintf"
+ * formatting, although the arguments are in a rather strange
+ * place. This is always a new message, there is no auto
+ * completion, and the return is echoed as such.
+ */
+/*VARARGS 0*/
+ereply(va_alist)
+va_dcl
+{
+ va_list pvar;
+ register char *fp, *buf;
+ register int nbuf;
+ register int i;
+
+ va_start(pvar);
+ fp = va_arg(pvar, char *);
+ buf = va_arg(pvar, char *);
+ nbuf = va_arg(pvar, int);
+ i = veread(fp, buf, nbuf, EFNEW|EFCR, &pvar);
+ va_end(pvar);
+ return i;
+}
+
+/*
+ * 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"). The "flag" contains EFNEW (a
+ * new prompt), an EFFUNC (autocomplete), or EFCR (echo
+ * the carriage return as CR).
+ */
+/* VARARGS 0 */
+eread(va_alist)
+va_dcl
+{
+ va_list pvar;
+ char *fp, *buf;
+ int nbuf, flag, i;
+ va_start(pvar);
+ fp = va_arg(pvar, char *);
+ buf = va_arg(pvar, char *);
+ nbuf = va_arg(pvar, int);
+ flag = va_arg(pvar, int);
+ i = veread(fp, buf, nbuf, flag, &pvar);
+ va_end(pvar);
+ return i;
+}
+
+static veread(fp, buf, nbuf, flag, ap) char *fp; char *buf; va_list *ap; {
+ register int cpos;
+ register int i;
+ register int c;
+
+#ifndef NO_MACRO
+ if(inmacro) {
+ bcopy(maclcur->l_text, buf, maclcur->l_used);
+ buf[maclcur->l_used] = '\0';
+ maclcur = maclcur->l_fp;
+ return TRUE;
+ }
+#endif
+ cpos = 0;
+ if ((flag&EFNEW)!=0 || ttrow!=nrow-1) {
+ ttcolor(CTEXT);
+ ttmove(nrow-1, 0);
+ epresf = TRUE;
+ } else
+ eputc(' ');
+ eformat(fp, ap);
+ tteeol();
+ ttflush();
+ for (;;) {
+ c = getkey(FALSE);
+ if ((flag&EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) {
+ cpos += complt(flag, c, buf, cpos);
+ continue;
+ }
+ if ((flag&EFAUTO) != 0 && c == '?') {
+ complt_list(flag, c, buf, cpos);
+ continue;
+ }
+ switch (c) {
+ case CCHR('J'):
+ c = CCHR('M'); /* and continue */
+ case CCHR('M'): /* Return, done. */
+ if ((flag&EFFUNC) != 0) {
+ if ((i = complt(flag, c, buf, cpos)) == 0)
+ continue;
+ if (i > 0) cpos += i;
+ }
+ buf[cpos] = '\0';
+ if ((flag&EFCR) != 0) {
+ ttputc(CCHR('M'));
+ ttflush();
+ }
+#ifndef NO_MACRO
+ if(macrodef) {
+ LINE *lp;
+
+ if((lp = lalloc(cpos)) == NULL) return FALSE;
+ lp->l_fp = maclcur->l_fp;
+ maclcur->l_fp = lp;
+ lp->l_bp = maclcur;
+ maclcur = lp;
+ bcopy(buf, lp->l_text, cpos);
+ }
+#endif
+ goto done;
+
+ case CCHR('G'): /* Bell, abort. */
+ eputc(CCHR('G'));
+ (VOID) ctrlg(FFRAND, 0);
+ ttflush();
+ return ABORT;
+
+ case CCHR('H'):
+ case CCHR('?'): /* Rubout, erase. */
+ if (cpos != 0) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ }
+ ttflush();
+ }
+ break;
+
+ case CCHR('X'): /* C-X */
+ case CCHR('U'): /* C-U, kill line. */
+ while (cpos != 0) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ }
+ }
+ ttflush();
+ break;
+
+ case CCHR('W'): /* C-W, kill to beginning of */
+ /* previous word */
+ /* back up to first word character or beginning */
+ while ((cpos > 0) && !ISWORD(buf[cpos - 1])) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ }
+ }
+ while ((cpos > 0) && ISWORD(buf[cpos - 1])) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ }
+ }
+ ttflush();
+ break;
+
+ case CCHR('\\'):
+ case CCHR('Q'): /* C-Q, quote next */
+ c = getkey(FALSE); /* and continue */
+ default: /* All the rest. */
+ if (cpos < nbuf-1) {
+ buf[cpos++] = (char) c;
+ eputc((char) c);
+ ttflush();
+ }
+ }
+ }
+done: return buf[0] != '\0';
+}
+
+/*
+ * do completion on a list of objects.
+ */
+static int complt(flags, c, buf, cpos)
+register char *buf;
+register int cpos;
+{
+ register LIST *lh, *lh2;
+ LIST *wholelist = NULL;
+ int i, nxtra;
+ int nhits, bxtra;
+ int wflag = FALSE;
+ int msglen, nshown;
+ char *msg;
+
+ if ((flags&EFFUNC) != 0) {
+ buf[cpos] = '\0';
+ i = complete_function(buf, c);
+ if(i>0) {
+ eputs(&buf[cpos]);
+ ttflush();
+ return i;
+ }
+ switch(i) {
+ case -3:
+ msg = " [Ambiguous]";
+ break;
+ case -2:
+ i=0;
+ msg = " [No match]";
+ break;
+ case -1:
+ case 0:
+ return i;
+ default:
+ msg = " [Internal error]";
+ break;
+ }
+ } else {
+ if ((flags&EFBUF) != 0) lh = &(bheadp->b_list);
+ else if ((flags&EFFILE) != 0) {
+ buf[cpos] = '\0';
+ wholelist = lh = make_file_list(buf,0);
+ }
+ 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;
+
+ while (lh != NULL) {
+ for (i=0; i<cpos; ++i) {
+ if (buf[i] != lh->l_name[i])
+ break;
+ }
+ if (i == cpos) {
+ if (nhits == 0)
+ lh2 = lh;
+ ++nhits;
+ if (lh->l_name[i] == '\0') nxtra = -1;
+ else {
+ bxtra = getxtra(lh, lh2, cpos, wflag);
+ if (bxtra < nxtra) nxtra = bxtra;
+ lh2 = lh;
+ }
+ }
+ lh = lh->l_next;
+ }
+ if (nhits == 0)
+ msg = " [No match]";
+ else if (nhits > 1 && nxtra == 0)
+ msg = " [Ambiguous]";
+ else { /* Got a match, do it to it */
+ /*
+ * 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; ++i) {
+ buf[cpos] = lh2->l_name[cpos];
+ eputc(buf[cpos++]);
+ }
+ ttflush();
+ free_file_list(wholelist);
+ if (nxtra < 0 && c != CCHR('M')) return 0;
+ return nxtra;
+ }
+ }
+ /* 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! */
+ return 0;
+}
+
+/*
+ * do completion on a list of objects, listing instead of completing
+ */
+static int complt_list(flags, c, buf, cpos)
+register char *buf;
+register int cpos;
+{
+ register LIST *lh, *lh2, *lh3;
+ LIST *wholelist = NULL;
+ int i,maxwidth,width;
+ int preflen = 0;
+ BUFFER *bp;
+ static VOID findbind();
+ int oldrow = ttrow;
+ int oldcol = ttcol;
+ int oldhue = tthue;
+ char linebuf[NCOL+1];
+ char *cp;
+
+ ttflush();
+
+ /* the results are put into a help buffer */
+
+ bp = bfind("*help*", TRUE);
+ if(bclear(bp) == FALSE) return FALSE;
+
+ { /* this {} present for historical reasons */
+
+/*
+ * 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, c);
+ }
+ else if ((flags&EFFILE) != 0) {
+ buf[cpos] = '\0';
+ wholelist = lh = make_file_list(buf,1);
+ /*
+ * 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) {
+ lh3 = lh2->l_next;
+ while (lh3) {
+ 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.
+ */
+ cp = linebuf;
+ width = 0;
+ lh2 = lh;
+ while (lh2 != NULL) {
+ for (i=0; i<cpos; ++i) {
+ if (buf[i] != lh2->l_name[i])
+ break;
+ }
+ if (i == cpos) {
+ if ((width + maxwidth) > ncol) {
+ *cp = 0;
+ addline(bp,linebuf);
+ cp = linebuf;
+ width = 0;
+ }
+ strcpy(cp,lh2->l_name+preflen);
+ i = strlen(lh2->l_name+preflen);
+ cp += i;
+ for (; i < maxwidth; i++)
+ *cp++ = ' ';
+ width += maxwidth;
+ }
+ lh2 = lh2->l_next;
+ }
+ if (width > 0) {
+ *cp = 0;
+ addline(bp,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); /* split the screen and put up the help buffer */
+ update(); /* 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.
+ */
+getxtra(lp1, lp2, cpos, wflag) register LIST *lp1, *lp2; register int wflag; {
+ register 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.
+ */
+/*VARARGS 0 */
+VOID
+ewprintf(va_alist)
+va_dcl
+{
+ va_list pvar;
+ register char *fp;
+
+#ifndef NO_MACRO
+ if(inmacro) return;
+#endif
+ va_start(pvar);
+ fp = va_arg(pvar, char *);
+ ttcolor(CTEXT);
+ ttmove(nrow-1, 0);
+ eformat(fp, &pvar);
+ va_end(pvar);
+ tteeol();
+ ttflush();
+ epresf = TRUE;
+}
+
+/*
+ * Printf style formatting. This is
+ * called by both "ewprintf" and "ereply" to provide
+ * formatting services to their 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.
+ * Note: %c works, and prints the "name" of the character.
+ * %k prints the name of a key (and takes no arguments).
+ */
+static VOID
+eformat(fp, ap)
+register char *fp;
+register va_list *ap;
+{
+ register int c;
+ char kname[NKNAME];
+ char *keyname();
+ char *cp;
+
+ while ((c = *fp++) != '\0') {
+ if (c != '%')
+ eputc(c);
+ else {
+ c = *fp++;
+ switch (c) {
+ case 'c':
+ (VOID) keyname(kname, va_arg(*ap, int));
+ eputs(kname);
+ break;
+
+ case 'k':
+ cp = kname;
+ for(c=0; c < key.k_count; c++) {
+ cp = keyname(cp, key.k_chars[c]);
+ *cp++ = ' ';
+ }
+ *--cp = '\0';
+ eputs(kname);
+ break;
+
+ case 'd':
+ eputi(va_arg(*ap, int), 10);
+ break;
+
+ case 'o':
+ eputi(va_arg(*ap, int), 8);
+ break;
+
+ case 's':
+ eputs(va_arg(*ap, char *));
+ break;
+
+ case 'l':/* explicit longword */
+ c = *fp++;
+ switch(c) {
+ case 'd':
+ eputl((long)va_arg(*ap, long), 10);
+ break;
+ default:
+ eputc(c);
+ break;
+ }
+ break;
+
+ default:
+ eputc(c);
+ }
+ }
+ }
+}
+
+/*
+ * Put integer, in radix "r".
+ */
+static VOID
+eputi(i, r)
+register int i;
+register int r;
+{
+ register 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(l, r)
+register long l;
+register int r;
+{
+ register 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(s)
+register char *s;
+{
+ register int c;
+
+ while ((c = *s++) != '\0')
+ eputc(c);
+}
+
+/*
+ * Put character. Watch for
+ * control characters, and for the line
+ * getting too long.
+ */
+static VOID
+eputc(c)
+register char c;
+{
+ if (ttcol+2 < ncol) {
+ if (ISCTRL(c)) {
+ eputc('^');
+ c = CCHR(c);
+ }
+ ttputc(c);
+ ++ttcol;
+ }
+}
+
+free_file_list(lp)
+ LIST *lp;
+{
+LIST *next;
+while (lp) {
+ next = lp->l_next;
+ free(lp);
+ lp = next;
+}
+}
+
+LIST *copy_list(lp)
+ LIST *lp;
+{
+LIST *current,*last;
+
+last = NULL;
+while(lp) {
+ current = (LIST *)malloc(sizeof(LIST));
+ current->l_next = last;
+ current->l_name = lp->l_name;
+ last = (LIST *)current;
+ lp = lp->l_next;
+}
+return(last);
+}
diff --git a/usr.bin/mg/extend.c b/usr.bin/mg/extend.c
new file mode 100644
index 00000000000..b3d4aa4a298
--- /dev/null
+++ b/usr.bin/mg/extend.c
@@ -0,0 +1,797 @@
+/*
+ * Extended (M-X) commands, rebinding, and
+ * startup file processing.
+ */
+#include "def.h"
+#include "kbd.h"
+
+#ifndef NO_MACRO
+#include "macro.h"
+#endif
+
+#ifdef FKEYS
+#include "key.h"
+#ifndef NO_STARTUP
+#ifndef BINDKEY
+#define BINDKEY /* bindkey is used by FKEYS startup code */
+#endif
+#endif
+#endif
+
+extern char *strncpy();
+extern int rescan();
+
+/* insert a string, mainly for use from macros (created by selfinsert) */
+/*ARGSUSED*/
+insert(f, n)
+int f, n;
+{
+ register char *cp;
+ char buf[128];
+#ifndef NO_MACRO
+ register int count;
+ int c;
+
+ if(inmacro) {
+ while(--n >= 0) {
+ for(count = 0; count < maclcur->l_used; count++) {
+ if((((c=maclcur->l_text[count]) == '\n') ? lnewline()
+ : linsert(1, c)) != TRUE) return FALSE;
+ }
+ }
+ maclcur = maclcur->l_fp;
+ return TRUE;
+ }
+ if(n==1) thisflag |= CFINS; /* CFINS means selfinsert can tack on end */
+#endif
+ if(eread("Insert: ", buf, sizeof(buf), EFNEW) == FALSE) return FALSE;
+ while(--n >= 0) {
+ cp = buf;
+ while(*cp) {
+ if(((*cp == '\n') ? 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 extremly 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 verses 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.
+ */
+static int remap(curmap, c, funct, pref_map)
+register KEYMAP *curmap;/* pointer to the map being changed */
+int c; /* character being changed */
+PF funct; /* function being changed to */
+KEYMAP *pref_map; /* if funct==prefix, map to bind to or NULL for new */
+/* extern MAP_ELEMENT *ele; must be set before calling */
+{
+ register int i;
+ int n1, n2, nold;
+ KEYMAP *mp;
+ PF *pfp;
+ MAP_ELEMENT *mep;
+ static KEYMAP *realocmap();
+
+ if(ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) {
+ if(ele > &curmap->map_element[0] && (funct!=prefix ||
+ (ele-1)->k_prefmap==NULL)) {
+ n1 = c - (ele-1)->k_num;
+ } else n1 = HUGE;
+ if(ele < &curmap->map_element[curmap->map_num] && (funct!=prefix ||
+ ele->k_prefmap==NULL)) {
+ n2 = ele->k_base - c;
+ } else n2 = HUGE;
+ if(n1 <= MAPELEDEF && n1 <= n2) {
+ ele--;
+ if((pfp = (PF *)malloc((unsigned)(c - ele->k_base+1)
+ * sizeof(PF))) == NULL) {
+ ewprintf("Out of memory");
+ return FALSE;
+ }
+ 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 = (PF *)malloc((unsigned)(ele->k_num - c + 1)
+ * sizeof(PF))) == NULL) {
+ ewprintf("Out of memory");
+ return FALSE;
+ }
+ 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 &&
+ (curmap = realocmap(curmap)) == NULL) return FALSE;
+ if((pfp = (PF *)malloc(sizeof(PF))) == NULL) {
+ ewprintf("Out of memory");
+ return FALSE;
+ }
+ 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 == prefix) {
+ if(pref_map != NULL) {
+ ele->k_prefmap = pref_map;
+ } else {
+ if((mp = (KEYMAP *)malloc(sizeof(KEYMAP) +
+ (MAPINIT-1)*sizeof(MAP_ELEMENT))) == NULL) {
+ ewprintf("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!=prefix || pref_map==NULL ||
+ pref_map==ele->k_prefmap))
+ return TRUE; /* no change */
+ if(funct!=prefix || ele->k_prefmap==NULL) {
+ if(ele->k_funcp[n1] == prefix)
+ ele->k_prefmap = (KEYMAP *)NULL;
+ ele->k_funcp[n1] = funct; /* easy case */
+ if(funct==prefix) {
+ if(pref_map!=NULL)
+ ele->k_prefmap = pref_map;
+ else {
+ if((mp = (KEYMAP *)malloc(sizeof(KEYMAP) +
+ (MAPINIT-1)*sizeof(MAP_ELEMENT))) == NULL) {
+ ewprintf("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] != prefix;
+ if(curmap->map_num >= curmap->map_max &&
+ (curmap = realocmap(curmap)) == NULL) return FALSE;
+ if((pfp = (PF *)malloc((unsigned)(ele->k_num - c + !n2)
+ * sizeof(PF))) == NULL) {
+ ewprintf("Out of memory");
+ return FALSE;
+ }
+ ele->k_funcp[n1] = prefix;
+ 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 = (KEYMAP *)malloc(sizeof(KEYMAP) +
+ (MAPINIT-1)*sizeof(MAP_ELEMENT))) == NULL) {
+ ewprintf("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, used above */
+static KEYMAP *realocmap(curmap)
+register KEYMAP *curmap;
+{
+ register KEYMAP *mp;
+ register int i;
+ static VOID fixmap();
+ extern int nmaps;
+
+ if((mp = (KEYMAP *)malloc((unsigned)(sizeof(KEYMAP)+
+ (curmap->map_max+(MAPGROW-1))*sizeof(MAP_ELEMENT)))) == NULL) {
+ ewprintf("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(i=nmaps; i--; ) {
+ if(map_table[i].p_map == curmap) map_table[i].p_map = mp;
+ else fixmap(curmap, mp, map_table[i].p_map);
+ }
+ ele = &mp->map_element[ele - &curmap->map_element[0]];
+ return mp;
+}
+
+/* fix references to a reallocated keymap (recursive) */
+static VOID fixmap(curmap, mp, mt)
+register KEYMAP *mt;
+register KEYMAP *curmap;
+KEYMAP *mp;
+{
+ register 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(curmap, p, unbind)
+register KEYMAP *curmap;
+char *p;
+int unbind;
+{
+ PF funct;
+ char prompt[80];
+ char *pep;
+ int c;
+ int s;
+ KEYMAP *pref_map = NULL;
+
+#ifndef NO_MACRO
+ if(macrodef) {
+ /* keystrokes arn't collected. Not hard, but pretty useless */
+ /* would not work for function keys in any case */
+ ewprintf("Can't rebind key in macro");
+ return FALSE;
+ }
+#ifndef NO_STARTUP
+ if(inmacro) {
+ for(s=0; s < maclcur->l_used - 1; s++) {
+ if(doscan(curmap, c=CHARMASK(maclcur->l_text[s])) != prefix) {
+ if(remap(curmap, c, prefix, (KEYMAP *)NULL) != TRUE) {
+ return FALSE;
+ }
+ }
+ curmap = ele->k_prefmap;
+ }
+ (VOID) doscan(curmap, c=maclcur->l_text[s]);
+ maclcur = maclcur->l_fp;
+ } else {
+#endif
+#endif
+ (VOID) strcpy(prompt, p);
+ pep = prompt + strlen(prompt);
+ for(;;) {
+ ewprintf("%s", prompt);
+ pep[-1] = ' ';
+ pep = keyname(pep, c = getkey(FALSE));
+ if(doscan(curmap,c) != prefix) break;
+ *pep++ = '-';
+ *pep = '\0';
+ curmap = ele->k_prefmap;
+ }
+#ifndef NO_STARTUP
+ }
+#endif
+ if(unbind) funct = rescan;
+ else {
+ if ((s=eread("%s to command: ", prompt, 80, EFFUNC|EFNEW, prompt))
+ != TRUE) return s;
+ if (((funct = name_function(prompt)) == prefix) ?
+ (pref_map = name_map(prompt)) == NULL : funct==NULL) {
+ ewprintf("[No match]");
+ return FALSE;
+ }
+ }
+ 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.
+*/
+#ifdef BINDKEY
+bindkey(mapp, fname, keys, kcount)
+KEYMAP **mapp;
+char *fname;
+KCHAR *keys;
+int kcount;
+{
+ KEYMAP *curmap = *mapp;
+ PF funct;
+ int c;
+ KEYMAP *pref_map = NULL;
+
+ if(fname == NULL) funct = rescan;
+ else if (((funct = name_function(fname)) == prefix) ?
+ (pref_map = name_map(fname)) == NULL : funct==NULL) {
+ ewprintf("[No match: %s]", fname);
+ return FALSE;
+ }
+ while(--kcount) {
+ if(doscan(curmap, c = *keys++) != prefix) {
+ if(remap(curmap, c, prefix, (KEYMAP *)NULL) != TRUE)
+ return FALSE;
+ }
+ curmap = ele->k_prefmap;
+ }
+ (VOID) doscan(curmap, c = *keys);
+ return remap(curmap, c, funct, pref_map);
+}
+#endif
+
+/*
+ * This function modifies the fundamental keyboard map.
+ */
+/*ARGSUSED*/
+bindtokey(f, n)
+{
+ return dobind(map_table[0].p_map, "Global set key: ", FALSE);
+}
+
+/*
+ * This function modifies the current mode's keyboard map.
+ */
+/*ARGSUSED*/
+localbind(f, n)
+{
+ return dobind(curbp->b_modes[curbp->b_nmodes]->p_map, "Local set key: ",
+ FALSE);
+}
+
+/*
+ * This function redefines a key in any keymap.
+ */
+/*ARGSUSED*/
+define_key(f, n)
+{
+ static char buf[48] = "Define key map: ";
+ MAPS *mp;
+ char *strncat();
+
+ buf[16] = '\0';
+ if(eread(buf, &buf[16], 48 - 16, EFNEW) != TRUE) return FALSE;
+ if((mp = name_mode(&buf[16])) == NULL) {
+ ewprintf("Unknown map %s", &buf[16]);
+ return FALSE;
+ }
+ (VOID) strncat(&buf[16], " key: ", 48-16-1);
+ return dobind(mp->p_map, buf, FALSE);
+}
+
+unbindtokey(f, n)
+int f, n;
+{
+ return dobind(map_table[0].p_map, "Global unset key: ", TRUE);
+}
+
+localunbind(f, n)
+int f, 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.
+ */
+extend(f, n)
+{
+ PF funct;
+ int s;
+ char xname[NXNAME];
+
+ if(!(f & FFARG)) s = eread("M-x ", xname, NXNAME, EFNEW|EFFUNC);
+ else s = eread("%d M-x ", xname, NXNAME, EFNEW|EFFUNC, n);
+ if(s != TRUE) return s;
+ if((funct = name_function(xname)) != NULL) {
+#ifndef NO_MACRO
+ if(macrodef) {
+ LINE *lp = maclcur;
+ macro[macrocount-1].m_funct = funct;
+ maclcur = lp->l_bp;
+ maclcur->l_fp = lp->l_fp;
+ free((char *)lp);
+ }
+#endif
+ return (*funct)(f, n);
+ }
+ ewprintf("[No match]");
+ return FALSE;
+}
+
+#ifndef NO_STARTUP
+/*
+ * 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.
+ */
+/*ARGSUSED*/
+evalexpr(f, n)
+{
+ int s;
+ char exbuf[128];
+
+ if ((s = ereply("Eval: ", exbuf, 128)) != TRUE)
+ return s;
+ return excline(exbuf);
+}
+/*
+ * evalbuffer - evaluate the current buffer as line commands. Useful
+ * for testing startup files.
+ */
+/*ARGSUSED*/
+evalbuffer(f, n)
+{
+ register LINE *lp;
+ register BUFFER *bp = curbp;
+ register int s;
+ static char excbuf[128];
+
+ for (lp = lforw(bp->b_linep); lp != bp->b_linep; lp = lforw(lp)) {
+ if (llength(lp) >= 128) return FALSE;
+ (VOID) strncpy(excbuf, ltext(lp), llength(lp));
+ excbuf[llength(lp)] = '\0'; /* make sure it's terminated */
+ if ((s = excline(excbuf)) != TRUE) return s;
+ }
+ 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*/
+evalfile(f, n)
+{
+ register int s;
+ char fname[NFILEN];
+
+ if ((s = ereply("Load file: ", fname, NFILEN)) != TRUE)
+ return s;
+ return load(fname);
+}
+
+/*
+ * load - go load the file name we got passed.
+ */
+load(fname) char *fname; {
+ int s = TRUE;
+ int nbytes;
+ char excbuf[128];
+
+ if ((fname = adjustname(fname)) == NULL)
+ return FALSE; /* just to be careful */
+
+ if (ffropen(fname, (BUFFER *) NULL) != FIOSUC) return FALSE;
+ while ((s = ffgetline(excbuf, sizeof(excbuf)-1, &nbytes)) == FIOSUC) {
+ excbuf[nbytes] = '\0';
+ if (excline(excbuf) != TRUE) {
+ s = FIOERR;
+ ewprintf("Error loading file %s", fname);
+ break;
+ }
+ }
+ (VOID) ffclose((BUFFER *) NULL);
+ excbuf[nbytes] = '\0';
+ if(s!=FIOEOF || (nbytes && excline(excbuf)!=TRUE))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * excline - run a line from a load file or eval-expression.
+ * if FKEYS is defined, duplicate functionallity of dobind so function
+ * key values don't have to fit in type char.
+ */
+excline(line)
+register char *line;
+{
+ register char *funcp, *argp = NULL;
+ register int c;
+ int status;
+ int f, n;
+ LINE *lp, *np;
+ PF fp;
+#ifdef FKEYS
+ int bind;
+ KEYMAP *curmap;
+ MAPS *mp;
+#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 */
+#else
+#define BINDEXT 0
+#endif
+ PF name_function();
+ LINE *lalloc();
+ static char *skipwhite(), *parsetoken();
+
+ if(macrodef || inmacro) {
+ ewprintf("Not now!");
+ return FALSE;
+ }
+
+ f = 0;
+ n = 1;
+ funcp = skipwhite(line);
+ if (*funcp == '\0') return TRUE; /* No error on blank lines */
+ line = parsetoken(funcp);
+ if (*line != '\0') {
+ *line++ = '\0';
+ line = skipwhite(line);
+ if ((*line >= '0' && *line <= '9') || *line == '-') {
+ argp = line;
+ line = parsetoken(line);
+ }
+ }
+
+ if (argp != NULL) {
+ f = FFARG;
+ n = atoi(argp);
+ }
+ if((fp = name_function(funcp)) == NULL) {
+ ewprintf("Unknown function: %s", funcp);
+ return FALSE;
+ }
+#ifdef FKEYS
+ if(fp == bindtokey || fp == unbindtokey) {
+ bind = BINDARG;
+ curmap = map_table[0].p_map;
+ } else if(fp == localbind || fp == localunbind) {
+ bind = BINDARG;
+ curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
+ } else if(fp == define_key) bind = BINDNEXT;
+ else bind = BINDNO;
+#endif
+ /* 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));
+#ifdef FKEYS
+ lp->l_used--; /* don't count BINDEXT! */
+ if(bind == BINDARG) bind = BINDNO;
+#endif
+ } else { /* Quoted strings special */
+ ++argp;
+#ifdef FKEYS
+ if(bind != BINDARG) {
+#endif
+ if((lp = lalloc((int)(line-argp)+BINDEXT))==NULL) {
+ status = FALSE;
+ goto cleanup;
+ }
+ lp->l_used = 0;
+#ifdef FKEYS
+ } else {
+ key.k_count = 0;
+ }
+#endif
+ 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;
+#ifdef FKEYS
+ case 'f': case 'F':
+ c = *++argp - '0';
+ if(ISDIGIT(argp[1])) {
+ c *= 10;
+ c += *++argp - '0';
+ }
+ c += KFIRST;
+ break;
+#endif
+ default:
+ c = CHARMASK(*argp);
+ break;
+ }
+ argp++;
+ }
+#ifdef FKEYS
+ if(bind == BINDARG)
+ key.k_chars[key.k_count++] = c;
+ else
+#endif
+ lp->l_text[lp->l_used++] = c;
+ }
+ if(*line) line++;
+ }
+#ifdef FKEYS
+ switch(bind) {
+ case BINDARG:
+ bind = BINDDO;
+ break;
+ case BINDNEXT:
+ lp->l_text[lp->l_used] = '\0';
+ if((mp = name_mode(lp->l_text)) == NULL) {
+ ewprintf("No such mode: %s", lp->l_text);
+ status = FALSE;
+ free((char *)lp);
+ goto cleanup;
+ }
+ curmap = mp->p_map;
+ free((char *)lp);
+ bind = BINDARG;
+ break;
+ default:
+#endif
+ lp->l_fp = np->l_fp;
+ lp->l_bp = np;
+ np->l_fp = lp;
+ np = lp;
+#ifdef FKEYS
+ }
+#endif
+ }
+#ifdef FKEYS
+ switch(bind) {
+ default:
+ ewprintf("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, (char *)NULL, key.k_chars, key.k_count);
+ break;
+ case BINDNO:
+#endif
+ inmacro = TRUE;
+ maclcur = maclcur->l_fp;
+ status = (*fp)(f, n);
+ inmacro = FALSE;
+#ifdef FKEYS
+ }
+#endif
+cleanup:
+ lp = maclcur->l_fp;
+ while(lp!=maclcur) {
+ np = lp->l_fp;
+ free((char *)lp);
+ lp = np;
+ }
+ free((char *)lp);
+ return status;
+}
+
+/*
+ * a pair of utility functions for the above
+ */
+static char *
+skipwhite(s)
+register char *s;
+{
+ while(*s == ' ' || *s == '\t' || *s == ')' || *s == '(') s++;
+ if (*s == ';') *s = '\0' ;
+ return s;
+}
+
+static char *
+parsetoken(s)
+register 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;
+}
+#endif
diff --git a/usr.bin/mg/file.c b/usr.bin/mg/file.c
new file mode 100644
index 00000000000..f75aa6a7e3a
--- /dev/null
+++ b/usr.bin/mg/file.c
@@ -0,0 +1,482 @@
+/*
+ * File commands.
+ */
+#include "def.h"
+
+BUFFER *findbuffer();
+VOID makename();
+VOID upmodes();
+static char *itos();
+
+/*
+ * insert a file into the current buffer. Real easy - just call the
+ * insertfile routine with the file name.
+ */
+/*ARGSUSED*/
+fileinsert(f, n)
+{
+ register int s;
+ char fname[NFILEN];
+
+ if ((s=eread("Insert file: ", fname, NFILEN, EFNEW|EFCR|EFFILE)) != TRUE)
+ return (s);
+ return insertfile(adjustname(fname), (char *) NULL, FALSE);
+ /* don't set buffer name */
+}
+
+/*
+ * Select a file for editing.
+ * Look around to see if you can find the
+ * fine 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*/
+filevisit(f, n)
+{
+ register BUFFER *bp;
+ int s;
+ char fname[NFILEN];
+ char *adjf;
+
+ if ((s=eread("Find file: ", fname, NFILEN, EFNEW|EFCR|EFFILE)) != TRUE)
+ return s;
+ adjf = adjustname(fname);
+ if ((bp = findbuffer(adjf)) == NULL) return FALSE;
+ curbp = bp;
+ if (showbuffer(bp, curwp, WFHARD) != TRUE) return FALSE;
+ if (bp->b_fname[0] == 0)
+ return readin(adjf); /* Read it in. */
+ return TRUE;
+}
+
+/*
+ * Pop to a file in the other window. Same as last function, just
+ * popbuf instead of showbuffer.
+ */
+/*ARGSUSED*/
+poptofile(f, n)
+{
+ register BUFFER *bp;
+ register WINDOW *wp;
+ int s;
+ char fname[NFILEN];
+ char *adjf;
+
+ if ((s=eread("Find file in other window: ", fname, NFILEN,
+ EFNEW|EFCR|EFFILE)) != TRUE)
+ return s;
+ adjf = adjustname(fname);
+ if ((bp = findbuffer(adjf)) == NULL) return FALSE;
+ if ((wp = popbuf(bp)) == NULL) return FALSE;
+ curbp = bp;
+ curwp = wp;
+ if (bp->b_fname[0] == 0)
+ return readin(adjf); /* Read it in. */
+ return TRUE;
+}
+
+/*
+ * given a file name, either find the buffer it uses, or create a new
+ * empty buffer to put it in.
+ */
+BUFFER *
+findbuffer(fname)
+char *fname;
+{
+ register BUFFER *bp;
+ char bname[NBUFN], *cp;
+ unsigned count = 1;
+
+ for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
+ if (fncmp(bp->b_fname, fname) == 0)
+ return bp;
+ }
+ makename(bname, fname); /* New buffer name. */
+ cp = bname + strlen(bname);
+ while(bfind(bname, FALSE) != NULL) {
+ *cp = '<'; /* add "<count>" to then name */
+ (VOID) strcpy(itos(cp, ++count)+1, ">");
+ }
+ return bfind(bname, TRUE);
+}
+
+/*
+ * Put the decimal representation of num into a buffer. Hacked to be
+ * faster, smaller, and less general.
+ */
+static char *itos(bufp, num)
+char *bufp;
+unsigned num;
+{
+ if (num >= 10) {
+ bufp = itos(bufp, num/10);
+ num %= 10;
+ }
+ *++bufp = '0' + num;
+ return bufp;
+}
+
+/*
+ * 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 "uemacs file").
+ */
+readin(fname) char *fname; {
+ register int status;
+ register WINDOW *wp;
+
+ if (bclear(curbp) != TRUE) /* Might be old. */
+ return TRUE;
+ status = insertfile(fname, fname, TRUE) ;
+ curbp->b_flag &= ~BFCHG; /* No change. */
+ for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
+ if (wp->w_bufp == curbp) {
+ wp->w_dotp = wp->w_linep = lforw(curbp->b_linep);
+ wp->w_doto = 0;
+ wp->w_markp = NULL;
+ wp->w_marko = 0;
+ }
+ }
+ 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. 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. If the
+ * BACKUP conditional is set, then 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;
+
+insertfile(fname, newname, needinfo) char fname[], newname[]; {
+ register LINE *lp1;
+ register LINE *lp2;
+ register WINDOW *wp;
+ int nbytes;
+ LINE *olp; /* Line we started at */
+ int opos; /* and offset into it */
+ int s, nline;
+ BUFFER *bp;
+
+ if (line == NULL) {
+ line = malloc(NLINE);
+ linesize = NLINE;
+ }
+ bp = curbp; /* Cheap. */
+ if (newname != (char *) NULL)
+ (VOID) strcpy(bp->b_fname, newname);
+ /* Hard file open. */
+ if ((s=ffropen(fname, needinfo ? bp : (BUFFER *) NULL)) == FIOERR)
+ goto out;
+ if (s == FIOFNF) { /* File not found. */
+ if (newname != NULL)
+ ewprintf("(New file)");
+ else ewprintf("(File not found)");
+ goto out;
+ }
+ opos = curwp->w_doto;
+ /* Open a new line, at point, and start inserting after it */
+ (VOID) lnewline();
+ olp = lback(curwp->w_dotp);
+ if(olp == curbp->b_linep) {
+ /* if at end of buffer, create a line to insert before */
+ (VOID) lnewline();
+ curwp->w_dotp = lback(curwp->w_dotp);
+ }
+ nline = 0; /* Don't count fake line at end */
+ while ((s=ffgetline(line, linesize, &nbytes)) != FIOERR) {
+doneread:
+ switch(s) {
+ case FIOSUC:
+ ++nline;
+ /* and continue */
+ case FIOEOF: /* the last line of the file */
+ if ((lp1=lalloc(nbytes)) == NULL) {
+ s = FIOERR; /* Keep message on the */
+ goto endoffile; /* display. */
+ }
+ 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) goto endoffile;
+ break;
+ case FIOLONG: { /* a line to long to fit in our buffer */
+ char *cp;
+ int newsize;
+
+ newsize = linesize * 2;
+ if(newsize < 0 ||
+ (cp = malloc((unsigned)newsize)) == NULL) {
+ ewprintf("Could not allocate %d bytes",
+ newsize);
+ s = FIOERR;
+ goto endoffile;
+ }
+ bcopy(line, cp, linesize);
+ free(line);
+ line = cp;
+ s=ffgetline(line+linesize, linesize, &nbytes);
+ nbytes += linesize;
+ linesize = newsize;
+ if (s == FIOERR)
+ goto endoffile;
+ goto doneread;
+ }
+ default:
+ ewprintf("Unknown code %d reading file", s);
+ s = FIOERR;
+ break;
+ }
+ }
+endoffile:
+ (VOID) ffclose((BUFFER *) NULL); /* Ignore errors. */
+ if (s==FIOEOF) { /* Don't zap an error. */
+ 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);
+ (VOID) ldelnewline();
+ curwp->w_dotp = olp;
+ curwp->w_doto = opos;
+ if(olp == curbp->b_linep) curwp->w_dotp = lforw(olp);
+#ifndef NO_BACKUP
+ if (newname != NULL)
+ bp->b_flag |= BFCHG | BFBAK; /* Need a backup. */
+ else bp->b_flag |= BFCHG;
+#else
+ bp->b_flag |= BFCHG;
+#endif
+ /* 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_linep;
+ if (curwp->w_markp == lp1) {
+ lp2 = curwp->w_dotp;
+ } else {
+ (VOID) ldelnewline(); /* delete extranious newline */
+out: lp2 = NULL;
+ }
+ for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
+ if (wp->w_bufp == curbp) {
+ wp->w_flag |= 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;
+ }
+ }
+ }
+ return s != FIOERR; /* False if error. */
+}
+
+/*
+ * Take a file name, and from it
+ * fabricate a buffer name. This routine knows
+ * about the syntax of file names on the target system.
+ * BDC1 left scan delimiter.
+ * BDC2 optional second left scan delimiter.
+ * BDC3 optional right scan delimiter.
+ */
+VOID
+makename(bname, fname) char bname[]; char fname[]; {
+ register char *cp1;
+ register char *cp2;
+
+ cp1 = &fname[0];
+ while (*cp1 != 0)
+ ++cp1;
+ --cp1; /* insure at least 1 character ! */
+#ifdef BDC2
+ while (cp1!=&fname[0] && cp1[-1]!=BDC1 && cp1[-1]!=BDC2)
+ --cp1;
+#else
+ while (cp1!=&fname[0] && cp1[-1]!=BDC1)
+ --cp1;
+#endif
+ cp2 = &bname[0];
+#ifdef BDC3
+ while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=BDC3)
+ *cp2++ = *cp1++;
+#else
+ while (cp2!=&bname[NBUFN-1] && *cp1!=0)
+ *cp2++ = *cp1++;
+#endif
+ *cp2 = 0;
+}
+
+/*
+ * 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 compatable with Gosling EMACS than
+ * with ITS EMACS.
+ */
+/*ARGSUSED*/
+filewrite(f, n)
+{
+ register int s;
+ char fname[NFILEN];
+ char *adjfname;
+
+ if ((s=eread("Write file: ", fname, NFILEN,
+ EFNEW|EFCR|EFFILE)) != TRUE)
+ return (s);
+ adjfname = adjustname(fname);
+ /* old attributes are no longer current */
+ bzero(&curbp->b_fi, sizeof(curbp->b_fi));
+ if ((s=writeout(curbp, adjfname)) == TRUE) {
+ (VOID) strcpy(curbp->b_fname, adjfname);
+#ifndef NO_BACKUP
+ curbp->b_flag &= ~(BFBAK | BFCHG);
+#else
+ curbp->b_flag &= ~BFCHG;
+#endif
+ upmodes(curbp);
+ }
+ return s;
+}
+
+/*
+ * Save the contents of the current buffer back into
+ * its associated file.
+ */
+#ifndef NO_BACKUP
+#ifndef MAKEBACKUP
+#define MAKEBACKUP TRUE
+#endif
+static int makebackup = MAKEBACKUP;
+#endif
+
+/*ARGSUSED*/
+filesave(f, n)
+{
+ 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.
+ */
+buffsave(bp) BUFFER *bp; {
+ register int s;
+
+ if ((bp->b_flag&BFCHG) == 0) { /* Return, no changes. */
+ ewprintf("(No changes need to be saved)");
+ return TRUE;
+ }
+ if (bp->b_fname[0] == '\0') { /* Must have a name. */
+ ewprintf("No file name");
+ return (FALSE);
+ }
+#ifndef NO_BACKUP
+ if (makebackup && (bp->b_flag&BFBAK)) {
+ s = fbackupfile(bp->b_fname);
+ if (s == ABORT) /* Hard error. */
+ return FALSE;
+ if (s == FALSE /* Softer error. */
+ && (s=eyesno("Backup error, save anyway")) != TRUE)
+ return s;
+ }
+#endif
+ if ((s=writeout(bp, bp->b_fname)) == TRUE) {
+#ifndef NO_BACKUP
+ bp->b_flag &= ~(BFCHG | BFBAK);
+#else
+ bp->b_flag &= ~BFCHG;
+#endif
+ upmodes(bp);
+ }
+ return s;
+}
+
+#ifndef NO_BACKUP
+/* 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. Only used when BACKUP
+ * is #defined.
+ */
+/*ARGSUSED*/
+makebkfile(f, n)
+{
+ if(f & FFARG) makebackup = n > 0;
+ else makebackup = !makebackup;
+ ewprintf("Backup files %sabled", makebackup ? "en" : "dis");
+ return TRUE;
+}
+#endif
+
+/*
+ * 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.
+ */
+writeout(bp, fn) register BUFFER *bp; char *fn; {
+ register int s;
+
+ if ((s=ffwopen(fn,bp)) != FIOSUC) /* Open writes message. */
+ return (FALSE);
+ s = ffputbuf(bp);
+ if (s == FIOSUC) { /* No write error. */
+ s = ffclose(bp);
+ if (s==FIOSUC)
+ ewprintf("Wrote %s", fn);
+ } else /* Ignore close error */
+ (VOID) ffclose(bp); /* if a write error. */
+ return s == FIOSUC;
+}
+
+/*
+ * Tag all windows for bp (all windows if bp NULL) as needing their
+ * mode line updated.
+ */
+VOID
+upmodes(bp) register BUFFER *bp; {
+ register WINDOW *wp;
+
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
+ if (bp == NULL || curwp->w_bufp == bp) wp->w_flag |= WFMODE;
+}
diff --git a/usr.bin/mg/fileio.c b/usr.bin/mg/fileio.c
new file mode 100644
index 00000000000..7f5ab6c34c4
--- /dev/null
+++ b/usr.bin/mg/fileio.c
@@ -0,0 +1,628 @@
+/*
+ * sys V fileio.c
+ */
+#include "def.h"
+
+static FILE *ffp;
+extern char *getenv();
+char *adjustname();
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+
+/*
+ * Open a file for reading.
+ */
+ffropen(fn, bp) char *fn; BUFFER *bp; {
+ struct stat statbuf;
+
+ if ((ffp=fopen(fn, "r")) == NULL)
+ return (FIOFNF);
+ if (bp && fstat(fileno(ffp), &statbuf) == 0) {
+/* set highorder bit to make sure this isn't all zero */
+ bp->b_fi.fi_mode = statbuf.st_mode | 0x8000;
+ bp->b_fi.fi_uid = statbuf.st_uid;
+ bp->b_fi.fi_gid = statbuf.st_gid;
+ }
+ return (FIOSUC);
+}
+
+/*
+ * Open a file for writing.
+ * Return TRUE if all is well, and
+ * FALSE on error (cannot create).
+ */
+ffwopen(fn, bp) char *fn; BUFFER *bp; {
+ if ((ffp=fopen(fn, "w")) == NULL) {
+ ewprintf("Cannot open file for writing");
+ 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' 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) {
+ chmod(fn, bp->b_fi.fi_mode & 07777);
+ chown(fn, bp->b_fi.fi_uid, bp->b_fi.fi_gid);
+ }
+ return (FIOSUC);
+}
+
+/*
+ * Close a file.
+ * Should look at the status.
+ */
+ffclose() {
+ (VOID) fclose(ffp);
+ return (FIOSUC);
+}
+
+/*
+ * Write a buffer to the already
+ * opened file. bp points to the
+ * buffer. Return the status.
+ * Check only at the newline and
+ * end of buffer.
+ */
+ffputbuf(bp)
+BUFFER *bp;
+{
+ register char *cp;
+ register char *cpend;
+ register LINE *lp;
+ register LINE *lpend;
+
+ lpend = bp->b_linep;
+ lp = lforw(lpend);
+ do {
+ cp = &ltext(lp)[0]; /* begining of line */
+ cpend = &cp[llength(lp)]; /* end of line */
+ while(cp != cpend) {
+ putc(*cp, ffp);
+ cp++; /* putc may evalualte arguments more than once */
+ }
+ lp = lforw(lp);
+ if(lp == lpend) break; /* no implied newline on last line */
+ putc('\n', ffp);
+ } while(!ferror(ffp));
+ if(ferror(ffp)) {
+ ewprintf("Write I/O error");
+ return FIOERR;
+ }
+ 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.
+ */
+ffgetline(buf, nbuf, nbytes)
+register char *buf;
+register int nbuf;
+register int *nbytes;
+{
+ register int c;
+ register int i;
+
+ i = 0;
+ while((c = getc(ffp))!=EOF && c!='\n') {
+ buf[i++] = c;
+ if (i >= nbuf) return FIOLONG;
+ }
+ if (c == EOF && ferror(ffp) != FALSE) {
+ ewprintf("File read error");
+ return FIOERR;
+ }
+ *nbytes = i;
+ return c==EOF ? FIOEOF : FIOSUC;
+}
+
+#ifndef NO_BACKUP
+/*
+ * Rename the file "fname" into a backup
+ * copy. 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". The "unlink" is perhaps not the
+ * right thing here; I don't care that much as
+ * I don't enable backups myself.
+ */
+fbackupfile(fn) char *fn; {
+ register char *nname;
+ char *malloc(), *strrchr();
+ int i;
+ char *lastpart;
+
+ if ((nname=malloc((unsigned)(strlen(fn)+1+1))) == NULL) {
+ ewprintf("Can't get %d bytes", strlen(fn) + 1);
+ return (ABORT);
+ }
+ (void) strcpy(nname, fn);
+/*
+ * with BSD, just strcat the ~. But SV has a max file name of 14, so
+ * we have to check.
+ */
+ lastpart = strrchr(nname, '/');
+ if (lastpart)
+ lastpart++;
+ else
+ lastpart = nname;
+ i = strlen(lastpart);
+ if (i > 13)
+ if (lastpart[13] == '~') { /* already a backup name */
+ free(nname);
+ return(FALSE);
+ }
+ else
+ lastpart[13] = '~';
+ else {
+ lastpart[i] = '~';
+ lastpart[i+1] = 0;
+ }
+ (void) unlink(nname); /* Ignore errors. */
+ if (link(fn, nname) != 0 || unlink(fn) != 0) {
+ free(nname);
+ return (FALSE);
+ }
+ free(nname);
+ return (TRUE);
+}
+#endif
+
+/*
+ * The string "fn" is a file name.
+ * Perform any required appending of directory name or case adjustments.
+ * If NO_DIR is not defined, the same file should be refered to even if the
+ * working directory changes.
+ */
+#ifdef SYMBLINK
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef MAXLINK
+#define MAXLINK 8 /* maximum symbolic links to follow */
+#endif
+#endif
+#include <pwd.h>
+#ifndef NO_DIR
+extern char *wdir;
+#endif
+
+char *adjustname(fn)
+register char *fn;
+{
+ register char *cp;
+ static char fnb[NFILEN];
+ extern struct passwd *getpwnam();
+ struct passwd *pwent;
+#ifdef SYMBLINK
+ struct stat statbuf;
+ int i, j;
+ char linkbuf[NFILEN];
+#endif
+
+ switch(*fn) {
+ case '/':
+ cp = fnb;
+ *cp++ = *fn++;
+ break;
+ case '~':
+ fn++;
+ if(*fn == '/' || *fn == '\0') {
+ (VOID) strcpy(fnb, getenv("HOME"));
+ cp = fnb + strlen(fnb);
+ if(*fn) fn++;
+ break;
+ } else {
+ cp = fnb;
+ while(*fn && *fn != '/') *cp++ = *fn++;
+ *cp = '\0';
+ if((pwent = getpwnam(fnb)) != NULL) {
+ (VOID) strcpy(fnb, pwent->pw_dir);
+ cp = fnb + strlen(fnb);
+ break;
+ } else {
+ fn -= strlen(fnb) + 1;
+ /* can't find ~user, continue to default case */
+ }
+ }
+ default:
+#ifndef NODIR
+ strcpy(fnb, wdir);
+ cp = fnb + strlen(fnb);
+ break;
+#else
+ return fn; /* punt */
+#endif
+ }
+ if(cp != fnb && cp[-1] != '/') *cp++ = '/';
+ while(*fn) {
+ switch(*fn) {
+ case '.':
+ switch(fn[1]) {
+ case '\0':
+ *--cp = '\0';
+ return fnb;
+ case '/':
+ fn += 2;
+ continue;
+ case '.':
+ if(fn[2]=='/' || fn[2] == '\0') {
+#ifdef SYMBLINK
+ cp[-1] = '\0';
+ for(j = MAXLINK; j-- &&
+ lstat(fnb, &statbuf) != -1 &&
+ (statbuf.st_mode&S_IFMT) == S_IFLNK &&
+ (i = readlink(fnb, linkbuf, sizeof linkbuf))
+ != -1 ;) {
+ if(linkbuf[0] != '/') {
+ --cp;
+ while(cp > fnb && *--cp != '/') {}
+ ++cp;
+ (VOID) strncpy(cp, linkbuf, i);
+ cp += i;
+ } else {
+ (VOID) strncpy(fnb, linkbuf, i);
+ cp = fnb + i;
+ }
+ if(cp[-1]!='/') *cp++ = '\0';
+ else cp[-1] = '\0';
+ }
+ cp[-1] = '/';
+#endif
+ --cp;
+ while(cp > fnb && *--cp != '/') {}
+ ++cp;
+ if(fn[2]=='\0') {
+ *--cp = '\0';
+ return fnb;
+ }
+ fn += 3;
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case '/':
+ fn++;
+ continue;
+ default:
+ break;
+ }
+ while(*fn && (*cp++ = *fn++) != '/') {}
+ }
+ if(cp[-1]=='/') --cp;
+ *cp = '\0';
+ return fnb;
+}
+
+#ifndef NO_STARTUP
+#include <sys/file.h>
+#define F_OK 04 /* for stupid Sys V */
+
+/*
+ * 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(suffix)
+char *suffix;
+{
+ register char *file;
+ static char home[NFILEN];
+ char *getenv();
+
+ if ((file = getenv("HOME")) == NULL) goto notfound;
+ if (strlen(file)+7 >= NFILEN - 1) goto notfound;
+ (VOID) strcpy(home, file);
+ (VOID) strcat(home, "/.mg");
+ if (suffix != NULL) {
+ (VOID) strcat(home, "-");
+ (VOID) strcat(home, suffix);
+ }
+ if (access(home, F_OK) == 0) return home;
+
+notfound:
+#ifdef STARTUPFILE
+ file = STARTUPFILE;
+ if (suffix != NULL) {
+ (VOID) strcpy(home, file);
+ (VOID) strcat(home, "-");
+ (VOID) strcat(home, suffix);
+ file = home;
+ }
+ if (access(file, F_OK ) == 0) return file;
+#endif
+
+ return NULL;
+}
+#endif
+
+#ifndef NO_DIRED
+#include "kbd.h"
+
+/*
+ * It's sort of gross to call system commands in a subfork. However
+ * that's by far the easiest way. These things are used only in
+ * dired, so they are not performance-critical. The cp program is
+ * almost certainly faster at copying files than any stupid code that
+ * we would write. In fact there is no other way to do unlinkdir.
+ * You don't want to do a simple unlink. To do the whole thing in
+ * a general way requires root, and rmdir is setuid. We don't really
+ * want microemacs to have to run setuid. rename is easy to do with
+ * unlink, link, unlink. However that code will fail if the destination
+ * is on a different file system. mv will copy in that case. It seems
+ * easier to let mv worry about this stuff.
+ */
+
+copy(frname, toname)
+char *frname, *toname;
+{
+ int pid;
+ int status;
+
+ if(pid = fork()) {
+ if(pid == -1) return -1;
+ execl("/bin/cp", "cp", frname, toname, (char *)NULL);
+ _exit(1); /* shouldn't happen */
+ }
+ while(wait(&status) != pid)
+ ;
+ return status == 0;
+}
+
+BUFFER *dired_(dirname)
+char *dirname;
+{
+ register BUFFER *bp;
+ char line[256];
+ BUFFER *findbuffer();
+ FILE *dirpipe;
+ FILE *popen();
+ char *strncpy();
+
+ if((dirname = adjustname(dirname)) == NULL) {
+ ewprintf("Bad directory name");
+ return NULL;
+ }
+ if((bp = findbuffer(dirname)) == NULL) {
+ ewprintf("Could not create buffer");
+ return NULL;
+ }
+ if(bclear(bp) != TRUE) return FALSE;
+ (VOID) strcpy(line, "ls -al ");
+ (VOID) strcpy(&line[7], dirname);
+ if((dirpipe = popen(line, "r")) == NULL) {
+ ewprintf("Problem opening pipe to ls");
+ return NULL;
+ }
+ line[0] = line[1] = ' ';
+ while(fgets(&line[2], 254, dirpipe) != NULL) {
+ line[strlen(line) - 1] = '\0'; /* remove ^J */
+ (VOID) addline(bp, line);
+ }
+ if(pclose(dirpipe) == -1) {
+ ewprintf("Problem closing pipe to ls");
+ return NULL;
+ }
+ bp->b_dotp = lforw(bp->b_linep); /* go to first line */
+ (VOID) strncpy(bp->b_fname, dirname, NFILEN);
+ if((bp->b_modes[0] = name_mode("dired")) == NULL) {
+ bp->b_modes[0] = &map_table[0];
+ ewprintf("Could not find mode dired");
+ return NULL;
+ }
+ bp->b_nmodes = 0;
+ return bp;
+}
+
+d_makename(lp, fn)
+register LINE *lp;
+register char *fn;
+{
+ register char *cp;
+
+ if(llength(lp) <= 56) return ABORT;
+ (VOID) strcpy(fn, curbp->b_fname);
+ cp = fn + strlen(fn);
+ bcopy(&lp->l_text[56], cp, llength(lp) - 56);
+ cp[llength(lp) - 56] = '\0';
+ return lgetc(lp, 2) == 'd';
+}
+
+/*
+ * I, a System V novice, could only figure out how to do unlinkdir()
+ * and rename() as exec's of the appropriate functions. So sue me.
+ * --Stephen Walton, December 1987
+ */
+
+unlinkdir(f)
+char *f;
+{
+ int status, pid, wpid;
+
+ if ((pid = fork()) == 0)
+ execl("/bin/rmdir", "rmdir", f, (char *)NULL);
+ else if (pid > 0)
+ while ((wpid = wait(&status)) && wpid != pid)
+ ;
+ else
+ return FALSE;
+ return status == 0;
+}
+
+Xrename(f1, f2)
+char *f1, *f2;
+{
+
+ int status, pid, wpid;
+
+ if ((pid = fork()) == 0)
+ execl("/bin/mv", "mv", f1, f2, (char *)NULL);
+ else if (pid > 0)
+ while ((wpid = wait(&status)) && wpid != pid)
+ ;
+ else
+ return FALSE;
+ return status == 0;
+}
+#endif NO_DIRED
+
+struct filelist {
+ LIST fl_l;
+ char fl_name[NFILEN+2];
+};
+
+/* these things had better be contiguous, because we're going
+ * to refer to the end of dirbuf + 1 byte */
+struct direct dirbuf;
+char dirdummy;
+
+/*
+ * return list of file names that match the name in buf.
+ * System V version. listing is a flag indicating whether the
+ * list is being used for printing a listing rather than
+ * completion. In that case, trailing * and / are put on
+ * for executables and directories. The list is not sorted.
+ */
+
+LIST *make_file_list(buf,listing)
+ char *buf;
+ int listing;
+{
+char *dir,*file,*cp;
+int len,i,preflen;
+int fp;
+LIST *last,*l1,*l2;
+struct filelist *current;
+char prefixx[NFILEN+1];
+extern int errno;
+struct stat statbuf;
+char statname[NFILEN+2];
+
+/* 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/.. as a reference to another directory,
+ * whereas we are interested in names starting with ..
+ */
+len = strlen(buf);
+if (buf[len-1] == '.') {
+ buf[len-1] = 'x';
+ dir = adjustname(buf);
+ buf[len-1] = '.';
+}
+else
+ dir = adjustname(buf);
+/*
+ * if the user typed a trailing / or the empty string
+ * he wants us to use his file spec as a directory name
+ */
+if (buf[0] && buf[strlen(buf)-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 */
+strcpy(prefixx,buf);
+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 + DIRSIZ) > NFILEN)
+ return(NULL);
+if ((strlen(dir) + DIRSIZ) > NFILEN)
+ listing = 0;
+
+/* 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.
+ */
+
+fp = open(dir,0);
+if (fp < 0) {
+ return(NULL);
+}
+
+last = NULL;
+/* clear entry after last so we can treat d_name as ASCIZ */
+dirbuf.d_name[DIRSIZ] = 0;
+while (1) {
+ if (read(fp, &dirbuf, sizeof(struct direct)) <= 0) {
+ break;
+ }
+ if (dirbuf.d_ino == 0) /* entry not allocated */
+ continue;
+ for (i=0; i<len; ++i) {
+ if (cp[i] != dirbuf.d_name[i])
+ break;
+ }
+ if (i < len)
+ continue;
+ current = (struct filelist *)malloc(sizeof(struct filelist));
+ current->fl_l.l_next = last;
+ current->fl_l.l_name = current->fl_name;
+ last = (LIST *)current;
+ strcpy(current->fl_name,prefixx);
+ strcat(current->fl_name,dirbuf.d_name);
+ if (listing) {
+ statbuf.st_mode = 0;
+ strcpy(statname,dir);
+ strcat(statname,"/");
+ strcat(statname,dirbuf.d_name);
+ stat(statname,&statbuf);
+ if (statbuf.st_mode & 040000)
+ strcat(current->fl_name,"/");
+ else if (statbuf.st_mode & 0100)
+ strcat(current->fl_name,"*");
+ }
+
+}
+close(fp);
+
+return (last);
+
+}
diff --git a/usr.bin/mg/help.c b/usr.bin/mg/help.c
new file mode 100644
index 00000000000..ae9268aa85a
--- /dev/null
+++ b/usr.bin/mg/help.c
@@ -0,0 +1,270 @@
+/* Help functions for MicroGnuEmacs 2 */
+
+#include "def.h"
+
+#ifndef NO_HELP
+#include "kbd.h"
+#include "key.h"
+#ifndef NO_MACRO
+#include "macro.h"
+#endif
+extern int rescan();
+
+/*
+ * 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*/
+desckey(f, n)
+{
+ register KEYMAP *curmap;
+ register PF funct;
+ register char *pep;
+ char prompt[80];
+ int c;
+ int m;
+ int i;
+
+#ifndef NO_MACRO
+ if(inmacro) return TRUE; /* ignore inside keyboard macro */
+#endif
+ (VOID) strcpy(prompt, "Describe key briefly: ");
+ pep = prompt + strlen(prompt);
+ key.k_count = 0;
+ m = curbp->b_nmodes;
+ curmap = curbp->b_modes[m]->p_map;
+ for(;;) {
+ for(;;) {
+ ewprintf("%s", prompt);
+ pep[-1] = ' ';
+ pep = keyname(pep, key.k_chars[key.k_count++] = c = getkey(FALSE));
+ if((funct = doscan(curmap, c)) != prefix) break;
+ *pep++ = '-';
+ *pep = '\0';
+ curmap = ele->k_prefmap;
+ }
+ if(funct != rescan) break;
+ if(ISUPPER(key.k_chars[key.k_count-1])) {
+ funct = doscan(curmap, TOLOWER(key.k_chars[key.k_count-1]));
+ if(funct == prefix) {
+ *pep++ = '-';
+ *pep = '\0';
+ curmap = ele->k_prefmap;
+ 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]);
+ if(funct != prefix) {
+ if(i == key.k_count - 1 && funct != rescan) goto found;
+ funct = rescan;
+ goto nextmode;
+ }
+ curmap = ele->k_prefmap;
+ }
+ *pep++ = '-';
+ *pep = '\0';
+ }
+found:
+ if(funct == rescan) ewprintf("%k is not bound to any function");
+ else if((pep = 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 MicroGnuEMACS produce it's own wall chart.
+ */
+static BUFFER *bp;
+static char buf[80]; /* used by showall and findbind */
+
+/*ARGSUSED*/
+wallchart(f, n)
+{
+ int m;
+ static char locbind[80] = "Local keybindings for mode ";
+ static int showall();
+
+ bp = bfind("*help*", TRUE);
+ if (bclear(bp) != TRUE) return FALSE; /* Clear it out. */
+ for(m=curbp->b_nmodes; m > 0; m--) {
+ (VOID) strcpy(&locbind[27], curbp->b_modes[m]->p_name);
+ (VOID) strcat(&locbind[27], ":");
+ if((addline(bp, locbind) == FALSE) ||
+ (showall(buf, curbp->b_modes[m]->p_map) == FALSE) ||
+ (addline(bp, "") == FALSE)) return FALSE;
+ }
+ if((addline(bp, "Global bindings:") == FALSE) ||
+ (showall(buf, map_table[0].p_map) == FALSE)) return FALSE;
+ return popbuftop(bp);
+}
+
+static int showall(ind, map)
+char *ind;
+KEYMAP *map;
+{
+ register MAP_ELEMENT *ele;
+ register int i;
+ PF functp;
+ char *cp;
+ char *cp2;
+ int last;
+
+ if(addline(bp, "") == FALSE) return FALSE;
+ last = -1;
+ for(ele = &map->map_element[0]; ele < &map->map_element[map->map_num] ; ele++) {
+ if(map->map_default != rescan && ++last < ele->k_base) {
+ cp = keyname(ind, last);
+ if(last < ele->k_base - 1) {
+ (VOID) strcpy(cp, " .. ");
+ cp = keyname(cp + 4, ele->k_base - 1);
+ }
+ do { *cp++ = ' '; } while(cp < &buf[16]);
+ (VOID) strcpy(cp, function_name(map->map_default));
+ if(addline(bp, buf) == FALSE) return FALSE;
+ }
+ last = ele->k_num;
+ for(i=ele->k_base; i <= last; i++) {
+ functp = ele->k_funcp[i - ele->k_base];
+ if(functp != rescan) {
+ if(functp != prefix) cp2 = function_name(functp);
+ else cp2 = map_name(ele->k_prefmap);
+ if(cp2 != NULL) {
+ cp = keyname(ind, i);
+ do { *cp++ = ' '; } while(cp < &buf[16]);
+ (VOID) strcpy(cp, cp2);
+ if (addline(bp, buf) == FALSE) return FALSE;
+ }
+ }
+ }
+ }
+ for(ele = &map->map_element[0]; ele < &map->map_element[map->map_num]; ele++) {
+ if(ele->k_prefmap != NULL) {
+ for(i = ele->k_base; ele->k_funcp[i - ele->k_base] != prefix; i++) {
+ if(i >= ele->k_num) /* damaged map */
+ return FALSE;
+ }
+ cp = keyname(ind, i);
+ *cp++ = ' ';
+ if(showall(cp, ele->k_prefmap) == FALSE) return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+help_help(f, n)
+int f, n;
+{
+ KEYMAP *kp;
+ PF funct;
+
+ if((kp = name_map("help")) == NULL) return FALSE;
+ ewprintf("a b c: ");
+ do {
+ funct = doscan(kp, getkey(FALSE));
+ } while(funct==NULL || funct==help_help);
+#ifndef NO_MACRO
+ if(macrodef && macrocount < MAXMACRO) macro[macrocount-1].m_funct = funct;
+#endif
+ return (*funct)(f, n);
+}
+
+static char buf2[128];
+static char *buf2p;
+
+/*ARGSUSED*/
+apropos_command(f, n)
+int f, n;
+{
+ register char *cp1, *cp2;
+ char string[32];
+ FUNCTNAMES *fnp;
+ BUFFER *bp;
+ static VOID findbind();
+
+ if(eread("apropos: ", string, sizeof(string), EFNEW) == ABORT) return ABORT;
+ /* FALSE means we got a 0 character string, which is fine */
+ bp = bfind("*help*", TRUE);
+ if(bclear(bp) == FALSE) return FALSE;
+ for(fnp = &functnames[0]; fnp < &functnames[nfunct]; fnp++) {
+ for(cp1 = fnp->n_name; *cp1; cp1++) {
+ cp2 = string;
+ while(*cp2 && *cp1 == *cp2)
+ cp1++, cp2++;
+ if(!*cp2) {
+ (VOID) strcpy(buf2, fnp->n_name);
+ buf2p = &buf2[strlen(buf2)];
+ findbind(fnp->n_funct, buf, map_table[0].p_map);
+ if(addline(bp, buf2) == FALSE) return FALSE;
+ break;
+ } else cp1 -= cp2 - string;
+ }
+ }
+ return popbuftop(bp);
+}
+
+static VOID findbind(funct, ind, map)
+PF funct;
+char *ind;
+KEYMAP *map;
+{
+ register MAP_ELEMENT *ele;
+ register int i;
+ char *cp;
+ int last;
+ static VOID bindfound();
+
+ last = -1;
+ for(ele = &map->map_element[0]; ele < &map->map_element[map->map_num]; ele++) {
+ if(map->map_default == funct && ++last < ele->k_base) {
+ cp = keyname(ind, last);
+ if(last < ele->k_base - 1) {
+ (VOID) strcpy(cp, " .. ");
+ (VOID) keyname(cp + 4, ele->k_base - 1);
+ }
+ bindfound();
+ }
+ last = ele->k_num;
+ for(i=ele->k_base; i <= last; i++) {
+ if(funct == ele->k_funcp[i - ele->k_base]) {
+ if(funct == prefix) {
+ cp = map_name(ele->k_prefmap);
+ if(!cp || strncmp(cp, buf2, strlen(cp)) != 0) continue;
+ }
+ (VOID) keyname(ind, i);
+ bindfound();
+ }
+ }
+ }
+ for(ele = &map->map_element[0]; ele < &map->map_element[map->map_num]; ele++) {
+ if(ele->k_prefmap != NULL) {
+ for(i = ele->k_base; ele->k_funcp[i - ele->k_base] != prefix; i++) {
+ if(i >= ele->k_num) return; /* damaged */
+ }
+ cp = keyname(ind, i);
+ *cp++ = ' ';
+ findbind(funct, cp, ele->k_prefmap);
+ }
+ }
+}
+
+static VOID bindfound() {
+ if(buf2p < &buf2[32]) {
+ do { *buf2p++ = ' '; } while(buf2p < &buf2[32]);
+ } else {
+ *buf2p++ = ',';
+ *buf2p++ = ' ';
+ }
+ (VOID) strcpy(buf2p, buf);
+ buf2p += strlen(buf);
+}
+#endif
diff --git a/usr.bin/mg/kbd.c b/usr.bin/mg/kbd.c
new file mode 100644
index 00000000000..3c2dd86da0c
--- /dev/null
+++ b/usr.bin/mg/kbd.c
@@ -0,0 +1,391 @@
+/*
+ * Terminal independent keyboard handling.
+ */
+#include "def.h"
+#include "kbd.h"
+
+#define EXTERN
+#include "key.h"
+
+#ifndef NO_MACRO
+#include "macro.h"
+#endif
+
+#ifdef DO_METAKEY
+#ifndef METABIT
+#define METABIT 0x80
+#endif
+
+int use_metakey = TRUE;
+
+/*
+ * Toggle the value of use_metakey
+ */
+do_meta(f, n)
+{
+ if(f & FFARG) use_metakey = n > 0;
+ else use_metakey = !use_metakey;
+ ewprintf("Meta keys %sabled", use_metakey ? "en" : "dis");
+ return TRUE;
+}
+#endif
+
+#ifdef BSMAP
+static int bs_map = BSMAP;
+/*
+ * Toggle backspace mapping
+ */
+bsmap(f, n)
+{
+ if(f & FFARG) bs_map = n > 0;
+ else bs_map = ! bs_map;
+ ewprintf("Backspace mapping %sabled", bs_map ? "en" : "dis");
+ return TRUE;
+}
+#endif
+
+#ifndef NO_DPROMPT
+#define PROMPTL 80
+ char prompt[PROMPTL], *promptp;
+#endif
+
+static int pushed = FALSE;
+static int pushedc;
+
+VOID ungetkey(c)
+int c;
+{
+#ifdef DO_METAKEY
+ if(use_metakey && pushed && c==CCHR('[')) pushedc |= METABIT;
+ else
+#endif
+ pushedc = c;
+ pushed = TRUE;
+}
+
+int getkey(flag)
+int flag;
+{
+ int c;
+ char *keyname();
+
+#ifndef NO_DPROMPT
+ if(flag && !pushed) {
+ if(prompt[0]!='\0' && ttwait()) {
+ ewprintf("%s", prompt); /* avoid problems with % */
+ update(); /* put the cursor back */
+ epresf = KPROMPT;
+ }
+ if(promptp > prompt) *(promptp-1) = ' ';
+ }
+#endif
+ if(pushed) {
+ c = pushedc;
+ pushed = FALSE;
+ } else c = getkbd();
+#ifdef BSMAP
+ if(bs_map)
+ if(c==CCHR('H')) c=CCHR('?');
+ else if(c==CCHR('?')) c=CCHR('H');
+#endif
+#ifdef DO_METAKEY
+ if(use_metakey && (c&METABIT)) {
+ pushedc = c & ~METABIT;
+ pushed = TRUE;
+ c = CCHR('[');
+ }
+#endif
+#ifndef NO_DPROMPT
+ if(flag && promptp < &prompt[PROMPTL - 5]) {
+ promptp = keyname(promptp, c);
+ *promptp++ = '-';
+ *promptp = '\0';
+ }
+#endif
+ 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.
+ */
+
+MAP_ELEMENT *ele;
+
+PF doscan(map, c)
+register KEYMAP *map;
+register int c;
+{
+ register MAP_ELEMENT *elec = &map->map_element[0]; /* local register copy for faster access */
+ register MAP_ELEMENT *last = &map->map_element[map->map_num];
+
+ while(elec < last && c > elec->k_num) elec++;
+ ele = elec; /* used by prefix and binding code */
+ if(elec >= last || c < elec->k_base)
+ return map->map_default;
+ return elec->k_funcp[c - elec->k_base];
+}
+
+doin()
+{
+ KEYMAP *curmap;
+ PF funct;
+
+#ifndef NO_DPROMPT
+ *(promptp = prompt) = '\0';
+#endif
+ 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))))
+ == prefix)
+ curmap = ele->k_prefmap;
+#ifndef NO_MACRO
+ if(macrodef && macrocount < MAXMACRO)
+ macro[macrocount++].m_funct = funct;
+#endif
+ return (*funct)(0, 1);
+}
+
+rescan(f, n)
+int f, n;
+{
+ int c;
+ register KEYMAP *curmap;
+ int i;
+ PF fp;
+ int mode = 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[mode]->p_map;
+ for(i=0; i < key.k_count-1; i++) {
+ if((fp=doscan(curmap,(key.k_chars[i]))) != prefix) break;
+ curmap = ele->k_prefmap;
+ }
+ if(fp==prefix) {
+ if((fp = doscan(curmap, c)) == prefix)
+ while((fp=doscan(curmap,key.k_chars[key.k_count++] =
+ getkey(TRUE))) == prefix)
+ curmap = ele->k_prefmap;
+ if(fp!=rescan) {
+#ifndef NO_MACRO
+ if(macrodef && macrocount <= MAXMACRO)
+ macro[macrocount-1].m_funct = fp;
+#endif
+ return (*fp)(f, n);
+ }
+ }
+ }
+ /* try previous mode */
+ if(--mode < 0) return ABORT;
+ curmap = curbp->b_modes[mode]->p_map;
+ for(i=0; i < key.k_count; i++) {
+ if((fp=doscan(curmap,(key.k_chars[i]))) != prefix) break;
+ curmap = ele->k_prefmap;
+ }
+ if(fp==prefix) {
+ while((fp=doscan(curmap,key.k_chars[i++]=getkey(TRUE)))
+ == prefix)
+ curmap = ele->k_prefmap;
+ key.k_count = i;
+ }
+ if(fp!=rescan && i>=key.k_count-1) {
+#ifndef NO_MACRO
+ if(macrodef && macrocount <= MAXMACRO)
+ macro[macrocount-1].m_funct = fp;
+#endif
+ return (*fp)(f, n);
+ }
+ }
+}
+
+universal_argument(f, n)
+int f, n;
+{
+ int c, nn=4;
+ KEYMAP *curmap;
+ PF funct;
+
+ 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)) == prefix) {
+ curmap = ele->k_prefmap;
+ key.k_chars[key.k_count++] = c = getkey(TRUE);
+ }
+ if(funct != universal_argument) {
+#ifndef NO_MACRO
+ if(macrodef && macrocount < MAXMACRO-1) {
+ if(f&FFARG) macrocount--;
+ macro[macrocount++].m_count = nn;
+ macro[macrocount++].m_funct = funct;
+ }
+#endif
+ return (*funct)(FFUNIV, nn);
+ }
+ nn <<= 2;
+ }
+}
+
+/*ARGSUSED*/
+digit_argument(f, n)
+int f, n;
+{
+ int nn, c;
+ KEYMAP *curmap;
+ PF funct;
+
+ 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)) == prefix) {
+ curmap = ele->k_prefmap;
+ key.k_chars[key.k_count++] = c = getkey(TRUE);
+ }
+#ifndef NO_MACRO
+ 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;
+ }
+#endif
+ return (*funct)(FFOTHARG, nn);
+}
+
+negative_argument(f, n)
+int f, n;
+{
+ int nn = 0, c;
+ KEYMAP *curmap;
+ PF funct;
+
+ 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)) == prefix) {
+ curmap = ele->k_prefmap;
+ key.k_chars[key.k_count++] = c = getkey(TRUE);
+ }
+#ifndef NO_MACRO
+ 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;
+ }
+#endif
+ return (*funct)(FFNEGARG, nn);
+}
+
+/*
+ * Insert a character. While defining a macro, create a "LINE" containing
+ * all inserted characters.
+ */
+
+selfinsert(f, n)
+int f, n;
+{
+ register int c;
+ int count;
+ VOID lchange();
+#ifndef NO_MACRO
+ LINE *lp;
+ int insert();
+#endif
+
+ if (n < 0) return FALSE;
+ if (n == 0) return TRUE;
+ c = key.k_chars[key.k_count-1];
+#ifndef NO_MACRO
+ if(macrodef && macrocount < MAXMACRO) {
+ if(f & FFARG) macrocount -= 2;
+ if(lastflag & CFINS) { /* last command was insert -- tack on end */
+ macrocount--;
+ if(maclcur->l_size < maclcur->l_used + n) {
+ if((lp = lallocx(maclcur->l_used + n)) == NULL)
+ return FALSE;
+ lp->l_fp = maclcur->l_fp;
+ lp->l_bp = maclcur->l_bp;
+ lp->l_fp->l_bp = lp->l_bp->l_fp = lp;
+ bcopy(maclcur->l_text, lp->l_text, maclcur->l_used);
+ for(count = maclcur->l_used; count < lp->l_used; count++)
+ lp->l_text[count] = c;
+ free((char *)maclcur);
+ maclcur = lp;
+ } else {
+ maclcur->l_used += n;
+ 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 = lallocx(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;
+ }
+#endif
+ if(c == '\n') {
+ do {
+ count = lnewline();
+ } while (--n && count==TRUE);
+ return count;
+ }
+ if(curbp->b_flag & BFOVERWRITE) { /* Overwrite mode */
+ 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);
+}
+
+/*
+ * this could be implemented as a keymap with everthing defined
+ * as self-insert.
+ */
+quote(f, n)
+{
+ register 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);
+}
diff --git a/usr.bin/mg/kbd.h b/usr.bin/mg/kbd.h
new file mode 100644
index 00000000000..1816fc6462c
--- /dev/null
+++ b/usr.bin/mg/kbd.h
@@ -0,0 +1,63 @@
+/*
+ * kbd.h: type definitions for symbol.c and kbd.c for mg experimental
+ */
+
+typedef struct {
+ 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 */
+} MAP_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;\
+ short map_max;\
+ PF map_default;\
+ MAP_ELEMENT map_element[NUM];\
+}
+ /* elements used */
+ /* elements allocated */
+ /* default function */
+ /* realy [e_max] */
+typedef struct keymap_s KEYMAPE(1) KEYMAP;
+
+#define none ctrlg
+#define prefix (PF)NULL
+
+/* number of map_elements to grow an overflowed keymap by */
+#define IMAPEXT 0
+#define MAPGROW 3
+#define MAPINIT (MAPGROW+1)
+
+/* max number of default bindings added to avoid creating new element */
+#define MAPELEDEF 4
+
+typedef struct MAPS_S {
+ KEYMAP *p_map;
+ char *p_name;
+} MAPS;
+
+extern MAPS map_table[];
+
+typedef struct {
+ PF n_funct;
+ char *n_name;
+} FUNCTNAMES;
+
+extern FUNCTNAMES functnames[];
+extern int nfunct;
+
+extern PF doscan();
+extern PF name_function();
+extern char *function_name();
+extern int complete_function();
+extern KEYMAP *name_map();
+extern char *map_name();
+extern MAPS *name_mode();
+
+extern MAP_ELEMENT *ele;
diff --git a/usr.bin/mg/key.h b/usr.bin/mg/key.h
new file mode 100644
index 00000000000..88e8bc1f1c7
--- /dev/null
+++ b/usr.bin/mg/key.h
@@ -0,0 +1,13 @@
+/* key.h: Insert file for mg 2 functions that need to reference key pressed */
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+#define MAXKEY 8 /* maximum number of prefix chars */
+
+EXTERN struct { /* the chacter sequence in a key */
+ int k_count; /* number of chars */
+ KCHAR k_chars[MAXKEY]; /* chars */
+} key;
+#undef EXTERN
diff --git a/usr.bin/mg/keymap.c b/usr.bin/mg/keymap.c
new file mode 100644
index 00000000000..d880bce016d
--- /dev/null
+++ b/usr.bin/mg/keymap.c
@@ -0,0 +1,1217 @@
+/*
+ * Keyboard maps. This is character set dependent.
+ * The terminal specific parts of building the
+ * keymap has been moved to a better place.
+ */
+#include "def.h"
+#include "kbd.h"
+
+/*
+ * Defined by "basic.c".
+ */
+extern int gotobol(); /* Move to start of line */
+extern int backchar(); /* Move backward by characters */
+extern int gotoeol(); /* Move to end of line */
+extern int forwchar(); /* Move forward by characters */
+extern int gotobob(); /* Move to start of buffer */
+extern int gotoeob(); /* Move to end of buffer */
+extern int forwline(); /* Move forward by lines */
+extern int backline(); /* Move backward by lines */
+extern int forwpage(); /* Move forward by pages */
+extern int backpage(); /* Move backward by pages */
+extern int pagenext(); /* Page forward next window */
+extern int setmark(); /* Set mark */
+extern int swapmark(); /* Swap "." and mark */
+extern int gotoline(); /* Go to a specified line. */
+#ifdef GOSMACS
+extern int forw1page(); /* move forward by lines */
+extern int back1page(); /* move back by lines */
+#endif
+
+/*
+ * Defined by "buffer.c".
+ */
+extern int listbuffers(); /* Display list of buffers */
+extern int usebuffer(); /* Switch a window to a buffer */
+extern int poptobuffer(); /* Other window to a buffer */
+extern int killbuffer(); /* Make a buffer go away. */
+extern int savebuffers(); /* Save unmodified buffers */
+extern int bufferinsert(); /* Insert buffer into another */
+extern int notmodified(); /* Reset modification flag */
+
+#ifndef NO_DIR
+/*
+ * Defined by "dir.c"
+ */
+extern int changedir(); /* change current directory */
+extern int showcwdir(); /* show current directory */
+
+#ifndef NO_DIRED
+/*
+ * defined by "dired.c"
+ */
+extern int dired(); /* dired */
+extern int d_findfile(); /* dired find file */
+extern int d_del(); /* dired mark for deletion */
+extern int d_undel(); /* dired unmark */
+extern int d_undelbak(); /* dired unmark backwards */
+extern int d_expunge(); /* dired expunge */
+extern int d_copy(); /* dired copy */
+extern int d_rename(); /* dired rename */
+extern int d_otherwindow(); /* dired other window */
+extern int d_ffotherwindow(); /* dired find file other window */
+#endif
+#endif
+
+/*
+ * Defined by "extend.c".
+ */
+extern int extend(); /* Extended commands. */
+extern int bindtokey(); /* Modify global key bindings. */
+extern int localbind(); /* Modify mode key bindings. */
+extern int define_key(); /* modify any key map */
+extern int unbindtokey(); /* delete global binding */
+extern int localunbind(); /* delete local binding */
+extern int insert(); /* insert string */
+#ifndef NO_STARTUP
+extern int evalexpr(); /* Extended commands (again) */
+extern int evalbuffer(); /* Evaluate current buffer */
+extern int evalfile(); /* Evaluate a file */
+#endif
+
+/*
+ * Defined by "file.c".
+ */
+extern int filevisit(); /* Get a file, read write */
+extern int poptofile(); /* Get a file, other window */
+extern int filewrite(); /* Write a file */
+extern int filesave(); /* Save current file */
+extern int fileinsert(); /* Insert file into buffer */
+#ifndef NO_BACKUP
+extern int makebkfile(); /* Control backups on saves */
+#endif
+
+/*
+ * defined by help.c
+ */
+#ifndef NO_HELP
+extern int desckey(); /* describe key */
+extern int wallchart(); /* Make wall chart. */
+extern int help_help(); /* help help */
+extern int apropos_command(); /* apropos */
+#endif
+
+/*
+ * defined by "kbd.c"
+ */
+#ifdef DO_METAKEY
+extern int do_meta(); /* interpret meta keys */
+#endif
+#ifdef BSMAP
+extern int bsmap(); /* backspace mapping */
+#endif
+extern int universal_argument(); /* Ctrl-U */
+extern int digit_argument(); /* M-1, etc. */
+extern int negative_argument(); /* M-- */
+extern int selfinsert(); /* Insert character */
+extern int rescan(); /* internal try again function */
+
+/*
+ * defined by "macro.c"
+ */
+#ifndef NO_MACRO
+extern int definemacro(); /* Begin macro */
+extern int finishmacro(); /* End macro */
+extern int executemacro(); /* Execute macro */
+#endif
+
+/*
+ * Defined by "main.c".
+ */
+extern int ctrlg(); /* Abort out of things */
+extern int quit(); /* Quit */
+
+/*
+ * Defined by "match.c"
+ */
+extern int showmatch(); /* Hack to show matching paren */
+
+/* defined by "modes.c" */
+
+extern int indentmode(); /* set auto-indent mode */
+extern int fillmode(); /* set word-wrap mode */
+extern int blinkparen(); /* Fake blink-matching-paren var */
+#ifdef NOTAB
+extern int notabmode(); /* no tab mode */
+#endif
+extern int overwrite(); /* overwrite mode */
+extern int set_default_mode(); /* set default modes */
+
+/*
+ * defined by "paragraph.c" - the paragraph justification code.
+ */
+extern int gotobop(); /* Move to start of paragraph. */
+extern int gotoeop(); /* Move to end of paragraph. */
+extern int fillpara(); /* Justify a paragraph. */
+extern int killpara(); /* Delete a paragraph. */
+extern int setfillcol(); /* Set fill column for justify. */
+extern int fillword(); /* Insert char with word wrap. */
+
+/*
+ * Defined by "random.c".
+ */
+extern int showcpos(); /* Show the cursor position */
+extern int twiddle(); /* Twiddle characters */
+extern int quote(); /* Insert literal */
+extern int openline(); /* Open up a blank line */
+extern int newline(); /* Insert newline */
+extern int deblank(); /* Delete blank lines */
+extern int justone(); /* Delete extra whitespace */
+extern int delwhite(); /* Delete all whitespace */
+extern int indent(); /* Insert newline, then indent */
+extern int forwdel(); /* Forward delete */
+extern int backdel(); /* Backward delete in */
+extern int killline(); /* Kill forward */
+extern int yank(); /* Yank back from killbuffer. */
+#ifdef NOTAB
+extern int space_to_tabstop();
+#endif
+
+#ifdef REGEX
+/*
+ * Defined by "re_search.c"
+ */
+extern int re_forwsearch(); /* Regex search forward */
+extern int re_backsearch(); /* Regex search backwards */
+extern int re_searchagain(); /* Repeat regex search command */
+extern int re_queryrepl(); /* Regex query replace */
+extern int setcasefold(); /* Set case fold in searches */
+extern int delmatchlines(); /* Delete all lines matching */
+extern int delnonmatchlines(); /* Delete all lines not matching */
+extern int cntmatchlines(); /* Count matching lines */
+extern int cntnonmatchlines(); /* Count nonmatching lines */
+#endif
+
+/*
+ * Defined by "region.c".
+ */
+extern int killregion(); /* Kill region. */
+extern int copyregion(); /* Copy region to kill buffer. */
+extern int lowerregion(); /* Lower case region. */
+extern int upperregion(); /* Upper case region. */
+#ifdef PREFIXREGION
+extern int prefixregion(); /* Prefix all lines in region */
+extern int setprefix(); /* Set line prefix string */
+#endif
+
+/*
+ * Defined by "search.c".
+ */
+extern int forwsearch(); /* Search forward */
+extern int backsearch(); /* Search backwards */
+extern int searchagain(); /* Repeat last search command */
+extern int forwisearch(); /* Incremental search forward */
+extern int backisearch(); /* Incremental search backwards */
+extern int queryrepl(); /* Query replace */
+
+/*
+ * Defined by "spawn.c".
+ */
+extern int spawncli(); /* Run CLI in a subjob. */
+extern int attachtoparent(); /* Attach to parent process */
+
+/* defined by "version.c" */
+
+extern int showversion(); /* Show version numbers, etc. */
+
+/*
+ * Defined by "window.c".
+ */
+extern int reposition(); /* Reposition window */
+extern int refresh(); /* Refresh the screen */
+extern int nextwind(); /* Move to the next window */
+#ifdef GOSMACS
+extern int prevwind(); /* Move to the previous window */
+#endif
+extern int onlywind(); /* Make current window only one */
+extern int splitwind(); /* Split current window */
+extern int delwind(); /* Delete current window */
+extern int enlargewind(); /* Enlarge display window. */
+extern int shrinkwind(); /* Shrink window. */
+
+/*
+ * Defined by "word.c".
+ */
+extern int backword(); /* Backup by words */
+extern int forwword(); /* Advance by words */
+extern int upperword(); /* Upper case word. */
+extern int lowerword(); /* Lower case word. */
+extern int capword(); /* Initial capitalize word. */
+extern int delfword(); /* Delete forward word. */
+extern int delbword(); /* Delete backward word. */
+
+#ifdef AMIGA
+#ifdef DO_ICONIFY
+extern int tticon();
+#endif
+#ifdef DO_MENU
+extern int amigamenu(); /* Menu function */
+#endif
+#ifdef MOUSE
+extern int amigamouse(); /* Amiga mouse functions */
+extern int mgotobob();
+extern int mforwdel();
+extern int mdelwhite();
+extern int mdelwind();
+extern int mgotoeob();
+extern int menlargewind();
+extern int mkillline();
+extern int mkillregion();
+extern int mdelfword();
+extern int mreposition();
+extern int mbackpage();
+extern int mforwpage();
+extern int mshrinkwind();
+extern int msplitwind();
+extern int myank();
+#endif MOUSE
+
+extern int togglewindow(); /* Defined by "ttyio.c" */
+extern int togglezooms(); /* "" "" */
+
+#ifdef CHANGE_FONT
+extern int setfont(); /* Defined by "ttyio.c" */
+#endif
+
+#ifdef CHANGE_COLOR
+ /* functions to mess with the mode line rendition, window colors*/
+extern int ttmode(); /* Defined by "tty.c" */
+extern int tttext(); /* "" */
+extern int textforeground(); /* "" */
+extern int textbackground(); /* "" */
+extern int modeforeground(); /* "" */
+extern int modebackground(); /* "" */
+#endif
+
+/*
+ * This file contains map segment definitions for adding function keys to
+ * keymap declarations. Currently you can add things to the fundamental
+ * mode keymap and the dired mode keymap. See the declaration of
+ * diredmap and fundmap for details.
+ */
+#include "amiga_maps.c"
+
+#endif /* AMIGA */
+
+/* initial keymap declarations, deepest first */
+
+#ifndef NO_HELP
+static PF cHcG[] = {
+ ctrlg, /* ^G */
+ help_help, /* ^H */
+};
+static PF cHa[] = {
+ apropos_command,/* a */
+ wallchart, /* b */
+ desckey, /* c */
+};
+static struct KEYMAPE(2+IMAPEXT) helpmap = {
+ 2,
+ 2+IMAPEXT,
+ rescan,
+ {
+ {CCHR('G'),CCHR('H'), cHcG, (KEYMAP *)NULL},
+ {'a', 'c', cHa, (KEYMAP *)NULL},
+ }
+};
+#endif
+
+static struct KEYMAPE(1+IMAPEXT) extramap1 = {
+ 0,
+ 1+IMAPEXT,
+ rescan
+};
+
+static struct KEYMAPE(1+IMAPEXT) extramap2 = {
+ 0,
+ 1+IMAPEXT,
+ rescan
+};
+
+static struct KEYMAPE(1+IMAPEXT) extramap3 = {
+ 0,
+ 1+IMAPEXT,
+ rescan
+};
+
+static struct KEYMAPE(1+IMAPEXT) extramap4 = {
+ 0,
+ 1+IMAPEXT,
+ rescan
+};
+
+static struct KEYMAPE(1+IMAPEXT) extramap5 = {
+ 0,
+ 1+IMAPEXT,
+ rescan
+};
+
+static PF cX4cF[] = {
+ poptofile, /* ^f */
+ ctrlg, /* ^g */
+};
+static PF cX4b[] = {
+ poptobuffer, /* b */
+ rescan, /* c */
+ rescan, /* d */
+ rescan, /* e */
+ poptofile, /* f */
+};
+static struct KEYMAPE(2+IMAPEXT) cX4map = {
+ 2,
+ 2+IMAPEXT,
+ rescan,
+ {
+ {CCHR('F'),CCHR('G'), cX4cF, (KEYMAP *)NULL},
+ {'b', 'f', cX4b, (KEYMAP *)NULL},
+ }
+};
+
+static PF cXcB[] = {
+ listbuffers, /* ^B */
+ quit, /* ^C */
+ rescan, /* ^D */
+ rescan, /* ^E */
+ filevisit, /* ^F */
+ ctrlg, /* ^G */
+};
+static PF cXcL[] = {
+ lowerregion, /* ^L */
+ rescan, /* ^M */
+ rescan, /* ^N */
+ deblank, /* ^O */
+ rescan, /* ^P */
+ rescan, /* ^Q */
+ rescan, /* ^R */
+ filesave, /* ^S */
+ rescan, /* ^T */
+ upperregion, /* ^U */
+ rescan, /* ^V */
+ filewrite, /* ^W */
+ swapmark, /* ^X */
+};
+#ifndef NO_MACRO
+static PF cXlp[] = {
+ definemacro, /* ( */
+ finishmacro, /* ) */
+};
+#endif
+static PF cX0[] = {
+ delwind, /* 0 */
+ onlywind, /* 1 */
+ splitwind, /* 2 */
+ rescan, /* 3 */
+ prefix, /* 4 */
+};
+static PF cXeq[] = {
+ showcpos, /* = */
+};
+static PF cXcar[] = {
+ enlargewind, /* ^ */
+ rescan, /* _ */
+ rescan, /* ` */
+ rescan, /* a */
+ usebuffer, /* b */
+ rescan, /* c */
+#ifndef NO_DIRED
+ dired, /* d */
+#else
+ rescan, /* d */
+#endif
+#ifndef NO_MACRO
+ executemacro, /* e */
+#else
+ rescan, /* e */
+#endif
+ setfillcol, /* f */
+ rescan, /* g */
+ rescan, /* h */
+ fileinsert, /* i */
+ rescan, /* j */
+ killbuffer, /* k */
+ rescan, /* l */
+ rescan, /* m */
+ rescan, /* n */
+ nextwind, /* o */
+ rescan, /* p */
+ rescan, /* q */
+ rescan, /* r */
+ savebuffers, /* s */
+};
+#ifndef NO_MACRO
+static struct KEYMAPE(6+IMAPEXT) cXmap = {
+ 6,
+ 6+IMAPEXT,
+#else
+static struct KEYMAPE(5+IMAPEXT) cXmap = {
+ 5,
+ 5+IMAPEXT,
+#endif
+ rescan,
+ {
+ {CCHR('B'),CCHR('G'), cXcB, (KEYMAP *)NULL},
+ {CCHR('L'),CCHR('X'), cXcL, (KEYMAP *)NULL},
+#ifndef NO_MACRO
+ {'(', ')', cXlp, (KEYMAP *)NULL},
+#endif
+ {'0', '4', cX0, (KEYMAP *)&cX4map},
+ {'=', '=', cXeq, (KEYMAP *)NULL},
+ {'^', 's', cXcar, (KEYMAP *)NULL},
+ }
+};
+
+static PF metacG[] = {
+ ctrlg, /* ^G */
+};
+static PF metacV[] = {
+ pagenext, /* ^V */
+};
+static PF metasp[] = {
+ justone, /* space */
+};
+static PF metapct[] = {
+ queryrepl, /* % */
+};
+static PF metami[] = {
+ negative_argument, /* - */
+ rescan, /* . */
+ 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 metalb[] = {
+ gotobop, /* [ */
+ delwhite, /* \ */
+ gotoeop, /* ] */
+ rescan, /* ^ */
+ rescan, /* _ */
+ rescan, /* ` */
+ rescan, /* a */
+ backword, /* b */
+ capword, /* c */
+ delfword, /* d */
+ rescan, /* e */
+ forwword, /* f */
+};
+static PF metal[] = {
+ lowerword, /* l */
+ rescan, /* m */
+ rescan, /* n */
+ rescan, /* o */
+ rescan, /* p */
+ fillpara, /* q */
+ backsearch, /* r */
+ forwsearch, /* s */
+ rescan, /* t */
+ upperword, /* u */
+ backpage, /* v */
+ copyregion, /* w */
+ extend, /* x */
+};
+static PF metatilde[] = {
+ notmodified, /* ~ */
+ delbword, /* DEL */
+};
+static struct KEYMAPE(8+IMAPEXT) metamap = {
+ 8,
+ 8+IMAPEXT,
+ rescan,
+ {
+ {CCHR('G'),CCHR('G'), metacG, (KEYMAP *)NULL},
+ {CCHR('V'),CCHR('V'), metacV, (KEYMAP *)NULL},
+ {' ', ' ', metasp, (KEYMAP *)NULL},
+ {'%', '%', metapct,(KEYMAP *)NULL},
+ {'-', '>', metami, (KEYMAP *)NULL},
+ {'[', 'f', metalb, (KEYMAP *)NULL},
+ {'l', 'x', metal, (KEYMAP *)NULL},
+ {'~', CCHR('?'), metatilde,(KEYMAP *)NULL},
+ }
+};
+
+static PF fund_at[] = {
+ setmark, /* ^@ */
+ gotobol, /* ^A */
+ backchar, /* ^B */
+ rescan, /* ^C */
+ forwdel, /* ^D */
+ gotoeol, /* ^E */
+ forwchar, /* ^F */
+ ctrlg, /* ^G */
+#ifndef NO_HELP
+ prefix, /* ^H */
+#else
+ rescan, /* ^H */
+#endif
+};
+/* ^I is selfinsert */
+static PF fund_CJ[] = {
+ indent, /* ^J */
+ killline, /* ^K */
+ reposition, /* ^L */
+ newline, /* ^M */
+ forwline, /* ^N */
+ openline, /* ^O */
+ backline, /* ^P */
+ quote, /* ^Q */
+ backisearch, /* ^R */
+ forwisearch, /* ^S */
+ twiddle, /* ^T */
+ universal_argument, /* ^U */
+ forwpage, /* ^V */
+ killregion, /* ^W */
+ prefix, /* ^X */
+ yank, /* ^Y */
+ attachtoparent, /* ^Z */
+};
+static PF fund_esc[] = {
+ prefix, /* esc */
+ rescan, /* ^\ */ /* selfinsert is default on fundamental */
+ rescan, /* ^] */
+ rescan, /* ^^ */
+ rescan, /* ^_ */
+};
+static PF fund_del[] = {
+ backdel, /* DEL */
+};
+
+#ifndef FUND_XMAPS
+#define NFUND_XMAPS 0 /* extra map sections after normal ones */
+#endif
+
+static struct KEYMAPE(4+NFUND_XMAPS+IMAPEXT) fundmap = {
+ 4 + NFUND_XMAPS,
+ 4 + NFUND_XMAPS + IMAPEXT,
+ selfinsert,
+ {
+#ifndef NO_HELP
+ {CCHR('@'),CCHR('H'), fund_at, (KEYMAP *)&helpmap},
+#else
+ {CCHR('@'),CCHR('H'), fund_at, (KEYMAP *)NULL},
+#endif
+ {CCHR('J'),CCHR('Z'), fund_CJ, (KEYMAP *)&cXmap},
+ {CCHR('['),CCHR('_'), fund_esc,(KEYMAP *)&metamap},
+ {CCHR('?'),CCHR('?'), fund_del,(KEYMAP *)NULL},
+#ifdef FUND_XMAPS
+ FUND_XMAPS,
+#endif
+ }
+};
+
+static PF fill_sp[] = {
+ fillword, /* ' ' */
+};
+static struct KEYMAPE(1+IMAPEXT) fillmap = {
+ 1,
+ 1+IMAPEXT,
+ rescan,
+ {
+ {' ', ' ', fill_sp, (KEYMAP *)NULL},
+ }
+};
+
+static PF indent_lf[] = {
+ newline, /* ^J */
+ rescan, /* ^K */
+ rescan, /* ^L */
+ indent, /* ^M */
+};
+static struct KEYMAPE(1+IMAPEXT) indntmap = {
+ 1,
+ 1+IMAPEXT,
+ rescan,
+ {
+ {CCHR('J'), CCHR('M'), indent_lf, (KEYMAP *)NULL},
+ }
+};
+static PF blink_rp[] = {
+ showmatch, /* ) */
+};
+static struct KEYMAPE(1+IMAPEXT) blinkmap = {
+ 1,
+ 1+IMAPEXT,
+ rescan,
+ {
+ {')', ')', blink_rp, (KEYMAP *)NULL},
+ }
+};
+
+#ifdef NOTAB
+static PF notab_tab[] = {
+ space_to_tabstop, /* ^I */
+};
+static struct KEYMAPE(1+IMAPEXT) notabmap = {
+ 1,
+ 1+IMAPEXT,
+ rescan,
+ {
+ {CCHR('I'),CCHR('I'), notab_tab, (KEYMAP *)NULL},
+ }
+};
+#endif
+
+static struct KEYMAPE(1+IMAPEXT) overwmap = {
+ 0,
+ 1+IMAPEXT, /* 1 to avoid 0 sized array */
+ rescan,
+ {
+ /* unused dummy entry for VMS C */
+ {(KCHAR)0, (KCHAR)0, (PF *)NULL, (KEYMAP *)NULL},
+ }
+};
+
+#ifndef NO_DIRED
+static PF dirednul[] = {
+ setmark, /* ^@ */
+ gotobol, /* ^A */
+ backchar, /* ^B */
+ rescan, /* ^C */
+ d_del, /* ^D */
+ gotoeol, /* ^E */
+ forwchar, /* ^F */
+ ctrlg, /* ^G */
+#ifndef NO_HELP
+ prefix, /* ^H */
+#endif
+};
+static PF diredcl[] = {
+ reposition, /* ^L */
+ forwline, /* ^M */
+ forwline, /* ^N */
+ rescan, /* ^O */
+ backline, /* ^P */
+ rescan, /* ^Q */
+ backisearch, /* ^R */
+ forwisearch, /* ^S */
+ rescan, /* ^T */
+ universal_argument, /* ^U */
+ forwpage, /* ^V */
+ rescan, /* ^W */
+ prefix, /* ^X */
+};
+static PF diredcz[] = {
+ attachtoparent, /* ^Z */
+ prefix, /* esc */
+ rescan, /* ^\ */
+ rescan, /* ^] */
+ rescan, /* ^^ */
+ rescan, /* ^_ */
+ forwline, /* SP */
+};
+static PF diredc[] = {
+ d_copy, /* c */
+ d_del, /* d */
+ d_findfile, /* e */
+ d_findfile, /* f */
+};
+static PF diredn[] = {
+ forwline, /* n */
+ d_ffotherwindow,/* o */
+ backline, /* p */
+ rescan, /* q */
+ d_rename, /* r */
+ rescan, /* s */
+ rescan, /* t */
+ d_undel, /* u */
+ rescan, /* v */
+ rescan, /* w */
+ d_expunge, /* x */
+};
+static PF direddl[] = {
+ d_undelbak, /* del */
+};
+
+#ifndef DIRED_XMAPS
+#define NDIRED_XMAPS 0 /* number of extra map sections */
+#endif
+
+static struct KEYMAPE(6 + NDIRED_XMAPS + IMAPEXT) diredmap = {
+ 6 + NDIRED_XMAPS,
+ 6 + NDIRED_XMAPS + IMAPEXT,
+ rescan,
+ {
+#ifndef NO_HELP
+ {CCHR('@'), CCHR('H'), dirednul, (KEYMAP *)&helpmap},
+#else
+ {CCHR('@'), CCHR('G'), dirednul, (KEYMAP *)NULL},
+#endif
+ {CCHR('L'), CCHR('X'), diredcl, (KEYMAP *)&cXmap},
+ {CCHR('Z'), ' ', diredcz, (KEYMAP *)&metamap},
+ {'c', 'f', diredc, (KEYMAP *)NULL},
+ {'n', 'x', diredn, (KEYMAP *)NULL},
+ {CCHR('?'), CCHR('?'), direddl, (KEYMAP *)NULL},
+#ifdef DIRED_XMAPS
+ DIRED_XMAPS, /* map sections for dired mode keys */
+#endif
+ }
+};
+#endif
+
+/* 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 dynamicly 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.
+ */
+
+MAPS map_table[] = {
+ /* fundamental map MUST be first entry */
+ {(KEYMAP *)&fundmap, "fundamental"},
+ {(KEYMAP *)&fillmap, "fill"},
+ {(KEYMAP *)&indntmap, "indent"},
+ {(KEYMAP *)&blinkmap, "blink"},
+#ifdef NOTAB
+ {(KEYMAP *)&notabmap, "notab"},
+#endif
+ {(KEYMAP *)&overwmap, "overwrite"},
+ {(KEYMAP *)&metamap, "esc prefix"},
+ {(KEYMAP *)&cXmap, "c-x prefix"},
+ {(KEYMAP *)&cX4map, "c-x 4 prefix"},
+ {(KEYMAP *)&extramap1, "extra prefix 1"},
+ {(KEYMAP *)&extramap2, "extra prefix 2"},
+ {(KEYMAP *)&extramap3, "extra prefix 3"},
+ {(KEYMAP *)&extramap4, "extra prefix 4"},
+ {(KEYMAP *)&extramap5, "extra prefix 5"},
+#ifndef NO_HELP
+ {(KEYMAP *)&helpmap, "help"},
+#endif
+#ifndef NO_DIRED
+ {(KEYMAP *)&diredmap, "dired"},
+#endif
+};
+
+#define NMAPS (sizeof map_table/sizeof(MAPS))
+int nmaps = NMAPS; /* for use by rebind in extend.c */
+
+char *map_name(map)
+KEYMAP *map;
+{
+ MAPS *mp = &map_table[0];
+
+ do {
+ if(mp->p_map == map) return mp->p_name;
+ } while(++mp < &map_table[NMAPS]);
+ return (char *)NULL;
+}
+
+MAPS *name_mode(name)
+char *name;
+{
+ MAPS *mp = &map_table[0];
+
+ do {
+ if(strcmp(mp->p_name,name)==0) return mp;
+ } while(++mp < &map_table[NMAPS]);
+ return (MAPS *)NULL;
+}
+
+KEYMAP *name_map(name)
+char *name;
+{
+ MAPS *mp;
+ return (mp=name_mode(name))==NULL ? (KEYMAP *)NULL : mp->p_map;
+}
+
+/* Warning: functnames MUST be in alphabetical order! (due to binary
+ * search in name_function.) If the function is prefix, it must be listed
+ * with the same name in the map_table above.
+ */
+
+FUNCTNAMES functnames[] = {
+#ifdef AMIGA
+#ifdef DO_ICONIFY
+ {tticon, "amiga-iconify"},
+#endif
+#ifdef DO_MENU
+ {amigamenu, "amiga-menu"},
+#endif
+#ifdef CHANGE_COLOR
+ {modebackground,"amiga-mode-background"},
+ {modeforeground,"amiga-mode-foreground"},
+ {ttmode, "amiga-mode-rendition"},
+#endif
+#ifdef CHANGE_FONT
+ {setfont, "amiga-set-font"},
+#endif
+#ifdef CHANGE_COLOR
+ {textbackground,"amiga-text-background"},
+ {textforeground,"amiga-text-foreground"},
+ {tttext, "amiga-text-rendition"},
+#endif
+ {togglewindow, "amiga-toggle-border"},
+ {togglezooms, "amiga-zoom-mode"},
+#endif /* AMIGA */
+#ifndef NO_HELP
+ {apropos_command, "apropos"},
+#endif
+ {fillmode, "auto-fill-mode"},
+ {indentmode, "auto-indent-mode"},
+ {backchar, "backward-char"},
+ {delbword, "backward-kill-word"},
+ {gotobop, "backward-paragraph"},
+ {backword, "backward-word"},
+ {gotobob, "beginning-of-buffer"},
+ {gotobol, "beginning-of-line"},
+ {blinkparen, "blink-matching-paren"},
+ {showmatch, "blink-matching-paren-hack"},
+#ifdef BSMAP
+ {bsmap, "bsmap-mode"},
+#endif
+ {prefix, "c-x 4 prefix"},
+ {prefix, "c-x prefix"},
+#ifndef NO_MACRO
+ {executemacro, "call-last-kbd-macro"},
+#endif
+ {capword, "capitalize-word"},
+#ifndef NO_DIR
+ {changedir, "cd"},
+#endif
+ {copyregion, "copy-region-as-kill"},
+#ifdef REGEX
+ {cntmatchlines, "count-matches"},
+ {cntnonmatchlines,"count-non-matches"},
+#endif
+ {define_key, "define-key"},
+ {backdel, "delete-backward-char"},
+ {deblank, "delete-blank-lines"},
+ {forwdel, "delete-char"},
+ {delwhite, "delete-horizontal-space"},
+#ifdef REGEX
+ {delmatchlines, "delete-matching-lines"},
+ {delnonmatchlines,"delete-non-matching-lines"},
+#endif
+ {onlywind, "delete-other-windows"},
+ {delwind, "delete-window"},
+#ifndef NO_HELP
+ {wallchart, "describe-bindings"},
+ {desckey, "describe-key-briefly"},
+#endif
+ {digit_argument,"digit-argument"},
+#ifndef NO_DIRED
+ {dired, "dired"},
+ {d_undelbak, "dired-backup-unflag"},
+ {d_copy, "dired-copy-file"},
+ {d_expunge, "dired-do-deletions"},
+ {d_findfile, "dired-find-file"},
+ {d_ffotherwindow, "dired-find-file-other-window"},
+ {d_del, "dired-flag-file-deleted"},
+ {d_otherwindow, "dired-other-window"},
+ {d_rename, "dired-rename-file"},
+ {d_undel, "dired-unflag"},
+#endif
+ {lowerregion, "downcase-region"},
+ {lowerword, "downcase-word"},
+ {showversion, "emacs-version"},
+#ifndef NO_MACRO
+ {finishmacro, "end-kbd-macro"},
+#endif
+ {gotoeob, "end-of-buffer"},
+ {gotoeol, "end-of-line"},
+ {enlargewind, "enlarge-window"},
+ {prefix, "esc prefix"},
+#ifndef NO_STARTUP
+ {evalbuffer, "eval-current-buffer"},
+ {evalexpr, "eval-expression"},
+#endif
+ {swapmark, "exchange-point-and-mark"},
+ {extend, "execute-extended-command"},
+ {prefix, "extra prefix 1"},
+ {prefix, "extra prefix 2"},
+ {prefix, "extra prefix 3"},
+ {prefix, "extra prefix 4"},
+ {prefix, "extra prefix 5"},
+ {fillpara, "fill-paragraph"},
+ {filevisit, "find-file"},
+ {poptofile, "find-file-other-window"},
+ {forwchar, "forward-char"},
+ {gotoeop, "forward-paragraph"},
+ {forwword, "forward-word"},
+ {bindtokey, "global-set-key"},
+ {unbindtokey, "global-unset-key"},
+ {gotoline, "goto-line"},
+#ifndef NO_HELP
+ {prefix, "help"},
+ {help_help, "help-help"},
+#endif
+ {insert, "insert"},
+ {bufferinsert, "insert-buffer"},
+ {fileinsert, "insert-file"},
+ {fillword, "insert-with-wrap"},
+ {backisearch, "isearch-backward"},
+ {forwisearch, "isearch-forward"},
+ {justone, "just-one-space"},
+ {ctrlg, "keyboard-quit"},
+ {killbuffer, "kill-buffer"},
+ {killline, "kill-line"},
+ {killpara, "kill-paragraph"},
+ {killregion, "kill-region"},
+ {delfword, "kill-word"},
+ {listbuffers, "list-buffers"},
+#ifndef NO_STARTUP
+ {evalfile, "load"},
+#endif
+ {localbind, "local-set-key"},
+ {localunbind, "local-unset-key"},
+#ifndef NO_BACKUP
+ {makebkfile, "make-backup-files"},
+#endif
+#ifdef DO_METAKEY
+ {do_meta, "meta-key-mode"}, /* better name, anyone? */
+#endif
+#ifdef AMIGA
+#ifdef MOUSE
+ {mgotobob, "mouse-beginning-of-buffer"},
+ {mforwdel, "mouse-delete-char"},
+ {mdelwhite, "mouse-delete-horizontal-space"},
+ {mdelwind, "mouse-delete-window"},
+ {mgotoeob, "mouse-end-of-buffer"},
+ {menlargewind, "mouse-enlarge-window"},
+ {mkillline, "mouse-kill-line"},
+ {mkillregion, "mouse-kill-region"},
+ {mdelfword, "mouse-kill-word"},
+ {mreposition, "mouse-recenter"},
+ {mbackpage, "mouse-scroll-down"},
+ {mforwpage, "mouse-scroll-up"},
+ {amigamouse, "mouse-set-point"},
+ {mshrinkwind, "mouse-shrink-window"},
+ {msplitwind, "mouse-split-window-vertically"},
+ {myank, "mouse-yank"},
+#endif
+#endif
+ {negative_argument, "negative-argument"},
+ {newline, "newline"},
+ {indent, "newline-and-indent"},
+ {forwline, "next-line"},
+#ifdef NOTAB
+ {notabmode, "no-tab-mode"},
+#endif
+ {notmodified, "not-modified"},
+ {openline, "open-line"},
+ {nextwind, "other-window"},
+ {overwrite, "overwrite-mode"},
+#ifdef PREFIXREGION
+ {prefixregion, "prefix-region"},
+#endif
+ {backline, "previous-line"},
+#ifdef GOSMACS
+ {prevwind, "previous-window"},
+#endif
+#ifdef VMS
+ {spawncli, "push-to-dcl"},
+#else
+ {spawncli, "push-shell"},
+#endif
+#ifndef NO_DIR
+ {showcwdir, "pwd"},
+#endif
+ {queryrepl, "query-replace"},
+#ifdef REGEX
+ {re_queryrepl, "query-replace-regexp"},
+#endif
+ {quote, "quoted-insert"},
+#ifdef REGEX
+ {re_searchagain,"re-search-again"},
+ {re_backsearch, "re-search-backward"},
+ {re_forwsearch, "re-search-forward"},
+#endif
+ {reposition, "recenter"},
+ {refresh, "redraw-display"},
+ {filesave, "save-buffer"},
+ {quit, "save-buffers-kill-emacs"},
+ {savebuffers, "save-some-buffers"},
+ {backpage, "scroll-down"},
+#ifdef GOSMACS
+ {back1page, "scroll-one-line-down"},
+ {forw1page, "scroll-one-line-up"},
+#endif
+ {pagenext, "scroll-other-window"},
+ {forwpage, "scroll-up"},
+ {searchagain, "search-again"},
+ {backsearch, "search-backward"},
+ {forwsearch, "search-forward"},
+ {selfinsert, "self-insert-command"},
+#ifdef REGEX
+ {setcasefold, "set-case-fold-search"},
+#endif
+ {set_default_mode, "set-default-mode"},
+ {setfillcol, "set-fill-column"},
+ {setmark, "set-mark-command"},
+#ifdef PREFIXREGION
+ {setprefix, "set-prefix-string"},
+#endif
+ {shrinkwind, "shrink-window"},
+#ifdef NOTAB
+ {space_to_tabstop, "space-to-tabstop"},
+#endif
+ {splitwind, "split-window-vertically"},
+#ifndef NO_MACRO
+ {definemacro, "start-kbd-macro"},
+#endif
+ {attachtoparent,"suspend-emacs"},
+ {usebuffer, "switch-to-buffer"},
+ {poptobuffer, "switch-to-buffer-other-window"},
+ {twiddle, "transpose-chars"},
+ {universal_argument, "universal-argument"},
+ {upperregion, "upcase-region"},
+ {upperword, "upcase-word"},
+ {showcpos, "what-cursor-position"},
+ {filewrite, "write-file"},
+ {yank, "yank"},
+};
+
+#define NFUNCT (sizeof(functnames)/sizeof(FUNCTNAMES))
+
+int nfunct = NFUNCT; /* used by help.c */
+
+/*
+ * The general-purpose version of ROUND2 blows osk C (2.0) out of the water.
+ * (reboot required) If you need to build a version of mg with less than 32
+ * or more than 511 functions, something better must be done.
+ * The version that should work, but doesn't is:
+ * #define ROUND2(x) (1+((x>>1)|(x>>2)|(x>>3)|(x>>4)|(x>>5)|(x>>6)|(x>>7)|\
+ * (x>>8)|(x>>9)|(x>>10)|(x>>11)|(x>>12)|(x>>13)|(x>>14)|(x>>15)))
+ */
+#define ROUND2(x) (x<128?(x<64?32:64):(x<256?128:256))
+
+static name_fent(fname, flag)
+register char *fname;
+int flag;
+{
+ register int try;
+ register int x = ROUND2(NFUNCT);
+ register int base = 0;
+ register int notit;
+
+ do {
+ /* + can be used instead of | here if more efficent. */
+ if((try = base | x) < NFUNCT) {
+ if((notit = strcmp(fname, functnames[try].n_name)) >= 0) {
+ if(!notit) return try;
+ base = try;
+ }
+ }
+ } while((x>>=1) || (try==1 && base==0)); /* try 0 once if needed */
+ return flag ? base : -1;
+}
+
+/*
+ * Translate from function name to function pointer, using binary search.
+ */
+
+PF name_function(fname)
+char *fname;
+{
+ int i;
+ if((i = name_fent(fname, FALSE)) >= 0) return functnames[i].n_funct;
+ return (PF)NULL;
+}
+
+/* complete function name */
+
+complete_function(fname, c)
+register char *fname;
+{
+ register int i, j, k, l;
+ int oj;
+
+ i = name_fent(fname, TRUE);
+ for(j=0; (l=fname[j]) && functnames[i].n_name[j]==l; j++) {}
+ if(fname[j]!='\0') {
+ if(++i >= NFUNCT) return -2; /* no match */
+ for(j=0; (l=fname[j]) && functnames[i].n_name[j]==l; j++) {}
+ if(fname[j]!='\0') return -2; /* no match */
+ }
+ if(c==CCHR('M') && functnames[i].n_name[j]=='\0') return -1;
+ for(k=i+1; k<NFUNCT; k++) { /* find last match */
+ for(l=0; functnames[k].n_name[l]==fname[l]; l++) {}
+ if(l<j) break;
+ }
+ k--;
+ oj = j;
+ if(k>i) { /* multiple matches */
+ while((l = functnames[i].n_name[j]) == functnames[k].n_name[j]) {
+ fname[j++] = l;
+ if(l=='-' && c==' ') break;
+ }
+ if(j==oj) return -3; /* ambiguous */
+ } else { /* single match */
+ while(l = functnames[i].n_name[j]) {
+ fname[j++] = l;
+ if(l=='-' && c==' ') break;
+ }
+ }
+ fname[j] = '\0';
+ return j - oj;
+}
+
+/* list possible function name completions */
+
+LIST *complete_function_list(fname, c)
+register char *fname;
+{
+ register int i, j, k, l;
+ int oj;
+ LIST *current,*last;
+
+ i = name_fent(fname, TRUE);
+ for(j=0; (l=fname[j]) && functnames[i].n_name[j]==l; j++) {}
+ if(fname[j]!='\0') {
+ if(++i >= NFUNCT) return NULL; /* no match */
+ for(j=0; (l=fname[j]) && functnames[i].n_name[j]==l; j++) {}
+ if(fname[j]!='\0') return NULL; /* no match */
+ }
+/*
+ * if(c==CCHR('M') && functnames[i].n_name[j]=='\0') return -1;
+ */
+ for(k=i+1; k<NFUNCT; k++) { /* find last match */
+ for(l=0; functnames[k].n_name[l]==fname[l]; l++) {}
+ if(l<j) break;
+ }
+ k--;
+ last = NULL;
+ for (; k >= i; k--) {
+ current = (LIST *)malloc(sizeof(LIST));
+ current->l_next = last;
+ current->l_name = functnames[k].n_name;
+ last = current;
+ }
+ return(last);
+}
+
+/* translate from function pointer to function name. */
+
+char *function_name(fpoint)
+register PF fpoint;
+{
+ register FUNCTNAMES *fnp = &functnames[0];
+
+ if(fpoint == prefix) return (char *)NULL; /* ambiguous */
+ do {
+ if(fnp->n_funct == fpoint) return fnp->n_name;
+ } while(++fnp < &functnames[NFUNCT]);
+ return (char *)NULL;
+}
diff --git a/usr.bin/mg/line.c b/usr.bin/mg/line.c
new file mode 100644
index 00000000000..3c892c706b1
--- /dev/null
+++ b/usr.bin/mg/line.c
@@ -0,0 +1,609 @@
+/*
+ * 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. There are routines
+ * in this file that handle the kill buffer too.
+ * It isn't here for any good reason.
+ *
+ * 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 being displayed, which means
+ * that "b_nwnd" is non zero, which means that the
+ * dot and mark values in the buffer headers are
+ * nonsense.
+ */
+#include "def.h"
+
+/* number of bytes member is from start of structure type */
+/* should be computed at compile time */
+
+#ifndef OFFSET
+#define OFFSET(type,member) ((char *)&(((type *)0)->member)-(char *)((type *)0))
+#endif
+
+#ifndef NBLOCK
+#define NBLOCK 16 /* Line block chunk size */
+#endif
+
+#ifndef KBLOCK
+#define KBLOCK 256 /* Kill buffer block size. */
+#endif
+
+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. */
+
+/*
+ * This routine allocates a block of memory large enough to hold a LINE
+ * containing "used" characters. The block is rounded up to whatever
+ * needs to be allocated. (use lallocx for lines likely to grow.)
+ * Return a pointer to the new block, or NULL if there isn't
+ * any memory left. Print a message in the message line if no space.
+ */
+LINE *
+lalloc(used) register int used; {
+ register LINE *lp;
+ register int size;
+
+ /* any padding at the end of the structure is used */
+ if((size = used + OFFSET(LINE, l_text[0])) < sizeof(LINE))
+ size = sizeof(LINE);
+#ifdef MALLOCROUND
+ MALLOCROUND(size); /* round up to a size optimal to malloc */
+#endif
+ if((lp = (LINE *)malloc((unsigned)size)) == NULL) {
+ ewprintf("Can't get %d bytes", size);
+ return (LINE *)NULL;
+ }
+ lp->l_size = size - OFFSET(LINE, l_text[0]);
+ lp->l_used = used;
+ return lp;
+}
+
+/*
+ * Like lalloc, only round amount desired up because this line will
+ * probably grow. We always make room for at least one more char.
+ * (thus making 0 not a special case anymore.)
+ */
+LINE *
+lallocx(used)
+int used;
+{
+ register int size;
+ register LINE *lp;
+
+ size = (NBLOCK+used) & ~(NBLOCK-1);
+ if((lp = lalloc(size)) != NULL) lp->l_used = used;
+ return lp;
+}
+
+/*
+ * Delete line "lp". Fix all of the
+ * links that might point at it (they are
+ * moved to offset 0 of the next line.
+ * Unlink the line from whatever buffer it
+ * might be in. Release the memory. The
+ * buffers are updated too; the magic conditions
+ * described in the above comments don't hold
+ * here.
+ */
+VOID
+lfree(lp) register LINE *lp; {
+ register BUFFER *bp;
+ register WINDOW *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((char *) lp);
+}
+
+/*
+ * This routine gets called when
+ * a character is changed 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(flag) register int flag; {
+ register WINDOW *wp;
+
+ if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
+ flag |= WFMODE; /* update mode lines. */
+ curbp->b_flag |= BFCHG;
+ }
+ for(wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_bufp == curbp) {
+ wp->w_flag |= flag;
+ if(wp != curwp) wp->w_flag |= WFHARD;
+ }
+ }
+}
+
+/*
+ * 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.
+ */
+linsert(n, c)
+int n;
+{
+ register char *cp1;
+ register char *cp2;
+ register LINE *lp1;
+ LINE *lp2;
+ LINE *lp3;
+ register int doto;
+ register RSIZE i;
+ WINDOW *wp;
+
+ lchange(WFEDIT);
+ lp1 = curwp->w_dotp; /* Current line */
+ if (lp1 == curbp->b_linep) { /* At the end: special */
+ /* (now should only happen in empty buffer */
+ if (curwp->w_doto != 0) {
+ ewprintf("bug: linsert");
+ return FALSE;
+ }
+ if ((lp2=lallocx(n)) == NULL) /* Allocate new line */
+ return FALSE;
+ lp3 = lp1->l_bp; /* Previous line */
+ lp3->l_fp = lp2; /* Link in */
+ 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;
+ }
+ /*NOSTRICT*/
+ curwp->w_doto = n;
+ return TRUE;
+ }
+ doto = curwp->w_doto; /* Save for later. */
+ /*NOSTRICT (2) */
+ if (lp1->l_used+n > lp1->l_size) { /* Hard: reallocate */
+ if ((lp2=lallocx(lp1->l_used+n)) == NULL)
+ return FALSE;
+ cp1 = &lp1->l_text[0];
+ cp2 = &lp2->l_text[0];
+ while (cp1 != &lp1->l_text[doto])
+ *cp2++ = *cp1++;
+ /*NOSTRICT*/
+ cp2 += n;
+ while (cp1 != &lp1->l_text[lp1->l_used])
+ *cp2++ = *cp1++;
+ lp1->l_bp->l_fp = lp2;
+ lp2->l_fp = lp1->l_fp;
+ lp1->l_fp->l_bp = lp2;
+ lp2->l_bp = lp1->l_bp;
+ free((char *) lp1);
+ } else { /* Easy: in place */
+ lp2 = lp1; /* Pretend new line */
+ /*NOSTRICT*/
+ lp2->l_used += n;
+ cp2 = &lp1->l_text[lp1->l_used];
+
+ cp1 = cp2-n;
+ while (cp1 != &lp1->l_text[doto])
+ *--cp2 = *--cp1;
+ }
+ for (i=0; i<n; ++i) /* Add the characters */
+ lp2->l_text[doto+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==curwp || wp->w_doto>doto)
+ /*NOSTRICT*/
+ wp->w_doto += n;
+ }
+ if (wp->w_markp == lp1) {
+ wp->w_markp = lp2;
+ if (wp->w_marko > doto)
+ /*NOSTRICT*/
+ wp->w_marko += n;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * Insert a newline into the buffer
+ * at the current location of dot in the current
+ * window. The funny ass-backwards way is no longer used.
+ */
+lnewline()
+{
+ register LINE *lp1;
+ register LINE *lp2;
+ register int doto;
+ register int nlen;
+ WINDOW *wp;
+
+ lchange(WFHARD);
+ lp1 = curwp->w_dotp; /* Get the address and */
+ doto = curwp->w_doto; /* offset of "." */
+ if(doto == 0) { /* avoid unnessisary copying */
+ if((lp2 = lallocx(0)) == NULL) /* new first part */
+ 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;
+ return TRUE;
+ }
+ nlen = llength(lp1) - doto; /* length of new part */
+ if((lp2=lallocx(nlen)) == NULL) /* New second half line */
+ 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;
+ for(wp = wheadp; wp != NULL; wp = wp->w_wndp) { /* Windows */
+ if (wp->w_dotp == lp1 && wp->w_doto >= doto) {
+ wp->w_dotp = lp2;
+ wp->w_doto -= doto;
+ }
+ if (wp->w_markp == lp1 && wp->w_marko >= doto) {
+ wp->w_markp = lp2;
+ wp->w_marko -= doto;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * This function deletes "n" bytes,
+ * starting at dot. It understands how do 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.
+ */
+ldelete(n, kflag) RSIZE n; {
+ register char *cp1;
+ register char *cp2;
+ register LINE *dotp;
+ register int doto;
+ register RSIZE chunk;
+ WINDOW *wp;
+
+ /*
+ * HACK - doesn't matter, and fixes back-over-nl bug for empty
+ * kill buffers.
+ */
+ if (kused == kstart) kflag = KFORW;
+
+ while (n != 0) {
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+ if (dotp == curbp->b_linep) /* Hit end of buffer. */
+ return FALSE;
+ chunk = dotp->l_used-doto; /* Size of chunk. */
+ if (chunk > n)
+ chunk = n;
+ if (chunk == 0) { /* End of line, merge. */
+ if(dotp == lback(curbp->b_linep))
+ return FALSE; /* End of buffer. */
+ lchange(WFHARD);
+ if (ldelnewline() == FALSE
+ || (kflag!=KNONE && kinsert('\n', kflag)==FALSE))
+ return FALSE;
+ --n;
+ continue;
+ }
+ lchange(WFEDIT);
+ cp1 = &dotp->l_text[doto]; /* Scrunch text. */
+ cp2 = cp1 + chunk;
+ if (kflag == KFORW) {
+ while (ksize - kused < chunk)
+ if (kgrow(FALSE) == FALSE) return FALSE;
+ bcopy(cp1, &(kbufp[kused]), (int) chunk);
+ kused += chunk;
+ } else if (kflag == KBACK) {
+ while (kstart < chunk)
+ if (kgrow(TRUE) == FALSE) return FALSE;
+ bcopy(cp1, &(kbufp[kstart-chunk]), (int) chunk);
+ kstart -= chunk;
+ } else if (kflag != KNONE) panic("broken ldelete call");
+ while (cp2 != &dotp->l_text[dotp->l_used])
+ *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) {
+ /*NOSTRICT*/
+ wp->w_doto -= chunk;
+ if (wp->w_doto < doto)
+ wp->w_doto = doto;
+ }
+ if (wp->w_markp==dotp && wp->w_marko>=doto) {
+ /*NOSTRICT*/
+ wp->w_marko -= chunk;
+ if (wp->w_marko < doto)
+ wp->w_marko = doto;
+ }
+ }
+ n -= chunk;
+ }
+ return TRUE;
+}
+
+/*
+ * Delete a newline. 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, and this makes
+ * the kill buffer work "right". 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.
+ */
+ldelnewline() {
+ register LINE *lp1;
+ register LINE *lp2;
+ register WINDOW *wp;
+ LINE *lp3;
+
+ lp1 = curwp->w_dotp;
+ lp2 = lp1->l_fp;
+ if (lp2 == curbp->b_linep) /* At the buffer end. */
+ return TRUE;
+ 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((char *) 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((char *) lp1);
+ free((char *) 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).
+ */
+lreplace(plen, st, f)
+register RSIZE plen; /* length to remove */
+char *st; /* replacement string */
+int f; /* case hack disable */
+{
+ register RSIZE rlen; /* replacement length */
+ register int rtype; /* capitalization */
+ register int c; /* used for random characters */
+ register int doto; /* offset into line */
+
+ /*
+ * Find the capitalization of the word that was found.
+ * f says use exact case of replacement string (same thing that
+ * happens with lowercase found), so bypass check.
+ */
+ /*NOSTRICT*/
+ (VOID) backchar(FFARG | FFRAND, (int) plen);
+ rtype = _L;
+ c = lgetc(curwp->w_dotp, curwp->w_doto);
+ if (ISUPPER(c)!=FALSE && f==FALSE) {
+ rtype = _U|_L;
+ if (curwp->w_doto+1 < llength(curwp->w_dotp)) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto+1);
+ if (ISUPPER(c) != FALSE) {
+ rtype = _U;
+ }
+ }
+ }
+
+ /*
+ * make the string lengths match (either pad the line
+ * so that it will fit, or scrunch out the excess).
+ * be careful with dot's offset.
+ */
+ rlen = strlen(st);
+ doto = curwp->w_doto;
+ if (plen > rlen)
+ (VOID) ldelete((RSIZE) (plen-rlen), KNONE);
+ else if (plen < rlen) {
+ if (linsert((int)(rlen-plen), ' ') == FALSE)
+ return FALSE;
+ }
+ curwp->w_doto = doto;
+
+ /*
+ * do the replacement: If was capital, then place first
+ * char as if upper, and subsequent chars as if lower.
+ * If inserting upper, check replacement for case.
+ */
+ while ((c = CHARMASK(*st++)) != '\0') {
+ if ((rtype&_U)!=0 && ISLOWER(c)!=0)
+ c = TOUPPER(c);
+ if (rtype == (_U|_L))
+ rtype = _L;
+ if (c == CCHR('J')) {
+ if (curwp->w_doto == llength(curwp->w_dotp))
+ (VOID) forwchar(FFRAND, 1);
+ else {
+ if (ldelete((RSIZE) 1, KNONE) != FALSE)
+ (VOID) lnewline();
+ }
+ } else if (curwp->w_dotp == curbp->b_linep) {
+ (VOID) linsert(1, c);
+ } else if (curwp->w_doto == llength(curwp->w_dotp)) {
+ if (ldelete((RSIZE) 1, KNONE) != FALSE)
+ (VOID) linsert(1, c);
+ } else
+ lputc(curwp->w_dotp, curwp->w_doto++, c);
+ }
+ lchange(WFHARD);
+ return (TRUE);
+}
+
+/*
+ * Delete all of the text
+ * saved in the kill buffer. Called by commands
+ * when a new kill context is being created. The kill
+ * buffer array is released, just in case the buffer has
+ * grown to immense size. No errors.
+ */
+VOID
+kdelete() {
+ if (kbufp != NULL) {
+ free((char *) 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.
+ */
+kinsert(c, dir) {
+
+ if (kused == ksize && dir == KFORW && kgrow(FALSE) == FALSE)
+ return FALSE;
+ if (kstart == 0 && dir == KBACK && kgrow(TRUE) == 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. back is true if
+ * we are trying to get space at the beginning of the kill buffer.
+ */
+kgrow(back) {
+ register int nstart;
+ register char *nbufp;
+
+ if ((unsigned)(ksize+KBLOCK) <= (unsigned)ksize) {
+ /* probably 16 bit unsigned */
+ ewprintf("Kill buffer size at maximum");
+ return FALSE;
+ }
+ if ((nbufp=malloc((unsigned)(ksize+KBLOCK))) == NULL) {
+ ewprintf("Can't get %ld bytes", (long)(ksize+KBLOCK));
+ return FALSE;
+ }
+ nstart = (back == TRUE) ? (kstart + KBLOCK) : (KBLOCK / 4) ;
+ bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (int) (kused-kstart));
+ if (kbufp != NULL)
+ free((char *) 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.
+ */
+kremove(n) {
+ if (n < 0 || n + kstart >= kused)
+ return -1;
+ return CHARMASK(kbufp[n + kstart]);
+}
diff --git a/usr.bin/mg/macro.c b/usr.bin/mg/macro.c
new file mode 100644
index 00000000000..94d14e28dc8
--- /dev/null
+++ b/usr.bin/mg/macro.c
@@ -0,0 +1,83 @@
+/* keyboard macros for MicroGnuEmacs 1x */
+
+#ifndef NO_MACRO
+#include "def.h"
+#include "key.h"
+#define EXTERN
+#define INIT(i) = (i)
+#include "macro.h"
+
+/*ARGSUSED*/
+definemacro(f, n)
+int f, n;
+{
+ register LINE *lp1;
+ LINE *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((char *)lp1);
+ }
+ free((char *)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*/
+finishmacro(f, n)
+int f, n;
+{
+ macrodef = FALSE;
+ ewprintf("End Keyboard Macro Definition");
+ return TRUE;
+}
+
+/*ARGSUSED*/
+executemacro(f, n)
+int f, n;
+{
+ int i, j;
+ PF funct;
+ int universal_argument();
+ int flag, num;
+
+ if(macrodef ||
+ (macrocount >= MAXMACRO && macro[MAXMACRO].m_funct != finishmacro))
+ 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;
+}
+#endif
diff --git a/usr.bin/mg/macro.h b/usr.bin/mg/macro.h
new file mode 100644
index 00000000000..9ce7f892f67
--- /dev/null
+++ b/usr.bin/mg/macro.h
@@ -0,0 +1,23 @@
+/* definitions for keyboard macros */
+
+#ifndef EXTERN
+#define EXTERN extern
+#define INIT(i)
+#endif
+
+#define MAXMACRO 256 /* maximum functs in a macro */
+
+EXTERN int inmacro INIT(FALSE);
+EXTERN int macrodef INIT(FALSE);
+EXTERN int macrocount INIT(0);
+
+EXTERN union {
+ PF m_funct;
+ int m_count; /* for count-prefix */
+} macro[MAXMACRO];
+
+EXTERN LINE *maclhead INIT(NULL);
+EXTERN LINE *maclcur;
+
+#undef EXTERN
+#undef INIT
diff --git a/usr.bin/mg/main.c b/usr.bin/mg/main.c
new file mode 100644
index 00000000000..6b5625af0b7
--- /dev/null
+++ b/usr.bin/mg/main.c
@@ -0,0 +1,143 @@
+/*
+ * Mainline
+ */
+#include "def.h"
+#ifndef NO_MACRO
+#include "macro.h"
+#endif
+
+int thisflag; /* Flags, this command */
+int lastflag; /* Flags, last command */
+int curgoal; /* Goal column */
+BUFFER *curbp; /* Current buffer */
+WINDOW *curwp; /* Current window */
+BUFFER *bheadp; /* BUFFER listhead */
+WINDOW *wheadp = (WINDOW *)NULL; /* WINDOW listhead */
+char pat[NPAT]; /* Pattern */
+#ifndef NO_DPROMPT
+extern char prompt[], *promptp; /* delayed prompting */
+#endif
+
+static VOID edinit();
+
+VOID
+main(argc, argv)
+int argc;
+char **argv;
+{
+#ifndef NO_STARTUP
+ char *startupfile();
+#endif
+ char *cp;
+ VOID vtinit(), makename(), eerase();
+ BUFFER *findbuffer();
+
+#ifdef SYSINIT
+ SYSINIT; /* system dependent. */
+#endif
+ vtinit(); /* Virtual terminal. */
+#ifndef NO_DIR
+ dirinit(); /* Get current directory */
+#endif
+ edinit(); /* Buffers, windows. */
+ ttykeymapinit(); /* Symbols, bindings. */
+ /* 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();
+#ifndef NO_STARTUP /* User startup file. */
+ if ((cp = startupfile((char *)NULL)) != NULL)
+ (VOID) load(cp);
+#endif
+ while (--argc > 0) {
+ cp = adjustname(*++argv);
+ curbp = findbuffer(cp);
+ (VOID) showbuffer(curbp, curwp, 0);
+ (VOID) readin(cp);
+ }
+ thisflag = 0; /* Fake last flags. */
+ for(;;) {
+#ifndef NO_DPROMPT
+ *(promptp = prompt) = '\0';
+ if(epresf == KPROMPT) eerase();
+#endif
+ update();
+ lastflag = thisflag;
+ thisflag = 0;
+ switch(doin()) {
+ case TRUE: break;
+ case ABORT:
+ ewprintf("Quit"); /* and fall through */
+ case FALSE:
+ default:
+ ttbeep();
+#ifndef NO_MACRO
+ macrodef = FALSE;
+#endif
+ }
+ }
+}
+
+/*
+ * Initialize default buffer and window.
+ */
+static VOID
+edinit() {
+ register BUFFER *bp;
+ register WINDOW *wp;
+
+ bheadp = NULL;
+ bp = bfind("*scratch*", TRUE); /* Text buffer. */
+ wp = (WINDOW *)malloc(sizeof(WINDOW)); /* Initial window. */
+ if (bp==NULL || wp==NULL) panic("edinit");
+ curbp = bp; /* Current ones. */
+ wheadp = wp;
+ curwp = wp;
+ wp->w_wndp = NULL; /* Initialize window. */
+ wp->w_bufp = bp;
+ bp->b_nwnd = 1; /* Displayed. */
+ wp->w_linep = wp->w_dotp = bp->b_linep;
+ wp->w_doto = 0;
+ wp->w_markp = NULL;
+ wp->w_marko = 0;
+ wp->w_toprow = 0;
+ wp->w_ntrows = nrow-2; /* 2 = mode, echo. */
+ wp->w_force = 0;
+ wp->w_flag = WFMODE|WFHARD; /* Full. */
+}
+
+/*
+ * 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*/
+quit(f, n)
+{
+ register int s;
+ VOID vttidy();
+
+ if ((s = anycb(FALSE)) == ABORT) return ABORT;
+ if (s == FALSE
+ || eyesno("Some modified buffers exist, really exit") == TRUE) {
+ vttidy();
+#ifdef SYSCLEANUP
+ SYSCLEANUP;
+#endif
+ exit(GOOD);
+ }
+ 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*/
+ctrlg(f, n)
+{
+ return ABORT;
+}
diff --git a/usr.bin/mg/match.c b/usr.bin/mg/match.c
new file mode 100644
index 00000000000..e49268b9b54
--- /dev/null
+++ b/usr.bin/mg/match.c
@@ -0,0 +1,189 @@
+/*
+ * Name: MicroEMACS
+ * 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.
+ *
+ * Since I often edit Scribe code, I've made it possible to
+ * blink arbitrary characters -- just bind delimiter characters
+ * to "blink-matching-paren-hack"
+ */
+#include "def.h"
+#include "key.h"
+
+static int balance();
+static VOID displaymatch();
+
+/* 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.
+ * This is to allow delimiters in Scribe documents to be matched.
+ */
+
+static struct balance {
+ char left, right;
+} bal[] = {
+ { '(', ')' },
+ { '[', ']' },
+ { '{', '}' },
+ { '<', '>' },
+ { '\0','\0'}
+};
+
+/*
+ * Self-insert character, then show matching character,
+ * if any. Bound to "blink-matching-paren-command".
+ */
+
+showmatch(f, n)
+{
+ register int i, s;
+
+ if (f & FFRAND) return FALSE;
+ for (i = 0; i < n; i++) {
+ if ((s = selfinsert(FFRAND, 1)) != TRUE)
+ return s;
+ if (balance() != TRUE) /* unbalanced -- warn user */
+ ttbeep();
+ }
+ 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 balance()
+{
+ register LINE *clp;
+ register int cbo;
+ int c;
+ int i;
+ int rbal, lbal;
+ int depth;
+
+ 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, since one was */
+ /* just self-inserted by blinkparen. */
+
+ clp = curwp->w_dotp;
+ cbo = curwp->w_doto - 1;
+
+ depth = 0; /* init nesting depth */
+
+ for (;;) {
+ if (cbo == 0) { /* beginning of line */
+ clp = lback(clp);
+ if (clp == curbp->b_linep)
+ return (FALSE);
+ cbo = llength(clp)+1;
+ }
+ if (--cbo == llength(clp)) /* end of line */
+ c = '\n';
+ 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(clp, cbo)
+register LINE *clp;
+register int cbo;
+{
+ register LINE *tlp;
+ register int tbo;
+ register int cp;
+ register int bufo;
+ register 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_flag |= WFMOVE;
+
+ update(); /* show match */
+ sleep(1); /* wait a bit */
+
+ curwp->w_dotp = tlp; /* return to old position */
+ curwp->w_doto = tbo;
+ curwp->w_flag |= WFMOVE;
+ update();
+ }
+ else { /* match not in this window so display line in echo area */
+ bufo = 0;
+ for (cp = 0; cp < llength(clp); cp++) { /* expand tabs */
+ 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);
+ }
+}
diff --git a/usr.bin/mg/mg.1 b/usr.bin/mg/mg.1
new file mode 100644
index 00000000000..a44951f19e7
--- /dev/null
+++ b/usr.bin/mg/mg.1
@@ -0,0 +1,139 @@
+.TH MG 1
+.SH NAME
+mg \- Micro Gnu emacs
+.SH SYNOPSIS
+.B mg
+[
+.I files
+]
+.SH DESCRIPTION
+.B Mg
+is intended as a micro version of Gnu Emacs. It is intended primarily
+for use on PC's of various kinds, where it may not be practical to
+run Gnu Emacs because of its size. However it is also useful on
+larger systems for some purposes. Because it is about 1/10 the size
+of Gnu Emacs, it starts much faster, and is much less likely to
+cause paging.
+.LP
+Normal editing commands should be identical to Gnu Emacs. It differs
+primarily in not having special modes for tasks other than straight
+editing, e.g. mail and news, and in not having special modes that
+support various programming languages. It does have text justification
+and auto-fill mode. It is written directly in C, so there is no
+language in which you can write extensions. However you can rebind
+keys and change some parameters. There are no limits to line length
+or format. Command, buffer, and file name completion and listing can
+be done using space and ? respectively.
+.LP
+.B Mg
+is close enough to Gnu Emacs that you can learn it the same way:
+using the program
+.BR teach-emacs .
+.B Teach-emacs
+will invoke Gnu Emacs, however the features that it teaches should
+work identically on
+.BR mg .
+.LP
+The one major difference is in configuration files. Gnu Emacs uses
+a configuration file
+.IR .emacs ,
+which is written in Lisp.
+.B Mg
+uses its own configuration files, which contain extend mode Emacs
+commands (i.e. commands that you could type after doing m-x).
+There are two configuration files,
+.IR .mg ,
+and
+.IR .mg-TERM .
+TERM here represents the name of you terminal type. E.g. if
+your terminal type is set to vt100,
+.B mg
+will use
+.I .mg-vt100
+as a startup file. The terminal type startup file is used
+first. If either of these files does not exist,
+.B mg
+will look for a file by the same name (but without the leading
+dot) in
+.IR /usr/local/lib/mg .
+.LP
+See the manual for a full list of the commands that can
+go in the files. The most commonly
+used ones are probably key binding. The following example is
+part of a configuration file used to set make
+.B mg
+respond to the keypad on a Microport SV/AT system. The normal
+keys send a sequence of the form <ESC> [ <letter>. I also
+want to use keys prefixed by <ESC> as having different meaning.
+In order to deal with multi-character sequences, the initial
+subsequences must be defined as prefixes. To allow for this,
+three prefixes are left undefined in the initial setup. They
+are called "extra prefix 1", etc.
+.br
+ ;allow normal pad
+.br
+ global-set-key ^[[ "extra prefix 1"
+.br
+ ;allow prefixed pad
+.br
+ global-set-key ^[^[ "extra prefix 2"
+.br
+ global-set-key ^[^[[ "extra prefix 3"
+.br
+ ;keypad
+.br
+ global-set-key ^[[A previous-line
+.br
+ global-set-key ^[[H scroll-down
+.br
+ ...etc
+.br
+ ;escaped keypad
+.br
+ global-set-key ^[^[[A exchange-point-and-mark
+.br
+ global-set-key ^[^[[H beginning-of-buffer
+.br
+ ...etc
+.LP
+Here's another example sequence that you may find useful. By default ()
+and [] are recognized as brackets, so bracket matching can be done.
+The following defines {} as brackets, and turns on the mode that causes
+the cursor to "blink" to show you matching brackets.
+.br
+ global-set-key } blink-matching-paren-hack
+.br
+ blink-matching-paren
+.br
+ set-default-mode blink
+.SH ARGUMENTS
+.B Mg
+does not take any options. The only arguments you can pass it are
+file names. It will do a find-file on each one, reading it into
+a buffer. It will leave the last buffer on the screen. If you call
+.B mg
+from
+.BR vnews ,
+both the original article and your reply will be in separate buffers.
+The original article will be showing. Use "c-x b" to switch to the
+buffer for your reply.
+.SH "SEE ALSO"
+.BR gnuemacs (1),
+.BR teach-emacs (1)
+.SH BUGS
+When you type ? to list possible file names, buffer names, etc.,
+a help buffer is created for the possibilities. In Gnu Emacs,
+this buffer goes away the next time you type a real command.
+In
+.BR mg ,
+you must use "m-x 1" to get rid of it.
+.SH FILES
+.LP
+.mg - normal startup file
+.LP
+.mg-TERM - terminal-specific startup file
+.LP
+/usr/local/lib/mg - directory for system-wide startup files. Files in
+this directory do not have the leading dot.
+.LP
+/usr/doc/mg.doc - full manual
diff --git a/usr.bin/mg/mg.doc b/usr.bin/mg/mg.doc
new file mode 100644
index 00000000000..fe238aee14d
--- /dev/null
+++ b/usr.bin/mg/mg.doc
@@ -0,0 +1,2202 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The MG Reference Manual
+
+ Release MG2A
+
+
+ Sandra J. Loosemore
+
+
+
+
+
+
+
+
+
+ Copyright (C)1987, Sandra J. Loosemore
+ This document, or sections of this document, may be freely
+
+ redistributed provided that the copyright notice and the following
+ disclaimer remain intact: The author bears no responsibilities for
+ errors in this document or the software it describes; and shall
+ not be held liable for any indirect, incidental, or consequential
+ damages.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Contents
+
+
+
+
+ 1 Introduction 2
+ 1.1 Implementations of MG. . . . . . . . . . . . . . . . . . . . . 3
+ 1.2 A Note on Character Sets . . . . . . . . . . . . . . . . . . . 3
+ 1.3 Notation and Conventions . . . . . . . . . . . . . . . . . . . 4
+ 1.4 Getting Started. . . . . . . . . . . . . . . . . . . . . . . . 5
+ 2 Using Commands 7
+ 2.1 Command Arguments. . . . . . . . . . . . . . . . . . . . . . . 7
+ 2.2 Prefix Arguments . . . . . . . . . . . . . . . . . . . . . . . 7
+ 2.3 Aborting . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
+ 2.4 Extended Commands. . . . . . . . . . . . . . . . . . . . . . . 8
+ 3 Moving the Cursor 9
+
+ 4 Text Insertion Commands 12
+ 5 Killing, Deleting, and Moving Text 14
+
+ 6 Searching and Replacing 16
+ 6.1 Searching. . . . . . . . . . . . . . . . . . . . . . . . . . . 16
+ 6.2 Replacing. . . . . . . . . . . . . . . . . . . . . . . . . . . 17
+ 6.3 Regular Expressions. . . . . . . . . . . . . . . . . . . . . . 18
+ 7 Windows 21
+
+ 8 Files and Buffers 23
+ 8.1 Buffer Manipulation. . . . . . . . . . . . . . . . . . . . . . 23
+ 8.2 Reading and Writing Files. . . . . . . . . . . . . . . . . . . 24
+ 8.3 Backup Files . . . . . . . . . . . . . . . . . . . . . . . . . 25
+ 8.4 Changing the Directory . . . . . . . . . . . . . . . . . . . . 25
+ 9 Modes 26
+ 9.1 No Tab Mode. . . . . . . . . . . . . . . . . . . . . . . . . . 26
+ 9.2 Overwrite Mode . . . . . . . . . . . . . . . . . . . . . . . . 27
+ 9.3 Auto Fill. . . . . . . . . . . . . . . . . . . . . . . . . . . 27
+ 9.4 Auto Indent. . . . . . . . . . . . . . . . . . . . . . . . . . 27
+ 9.5 Blink. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
+
+
+ 1
+
+
+
+
+
+
+
+ 9.6 Dired Mode . . . . . . . . . . . . . . . . . . . . . . . . . . 28
+
+ 10 Miscellaneous 30
+ 10.1 Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
+ 10.2 Keyboard Macros. . . . . . . . . . . . . . . . . . . . . . . . 30
+ 10.3 Changing Case. . . . . . . . . . . . . . . . . . . . . . . . . 31
+ 10.4 Odds and Ends. . . . . . . . . . . . . . . . . . . . . . . . . 31
+ 11 Customization 33
+ 11.1 Key Bindings . . . . . . . . . . . . . . . . . . . . . . . . . 33
+ 11.2 Startup Files. . . . . . . . . . . . . . . . . . . . . . . . . 34
+Fundamental Mode Key Bindings 36
+
+Index 38
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 1
+
+
+
+
+Introduction
+
+
+
+MG is a small, fast, and portable Emacs-style text editor intended to be
+used by people who can't run a real Emacs for one reason or another --- as
+their main editor on smaller machines with limited memory or file space, or
+as a ``quick-start'' editor on larger systems, useful for composing short
+mail messages and the like.
+ We've made MG compatible with GNU Emacs because that is the ``big'',
+full-featured editor that many of us use regularly and are most familiar
+with. GNU Emacs is the creation of Richard M. Stallman, who was also the
+author of the original Emacs editor. However, MG is not associated in any
+way with the GNU project, and the MG authors individually may or may not
+agree with the opinions expressed by Richard Stallman and the GNU project.
+ MG is largely public domain. You can use, modify, and redistribute MG
+as you like. A few modules, however, are copyrighted; specifically, the
+regular expression code, the VMS termcap routines, and the Amiga support
+code. Look at the source code for the exact copyright restrictions.
+ There are several other editors in existence which call themselves
+MicroEmacs. The original public domain version was written by Dave Conroy
+and circulated as version 1.6. Derived from this, there is another PD
+version by Dave Conroy numbered v30; a significantly larger PD version by
+Daniel Lawrence which is now up to version 3.9; at least one proprietary
+implementation; an implementation for the Atari ST with an integrated
+command shell, by Prabhaker Mateti; and probably others that we don't know
+about.
+ MG is derived from the v30 MicroEmacs, with key bindings, command names,
+and general functionality made more compatible with GNU Emacs. Like v30,
+MG is fairly small and quite robust. We have generally resisted the
+temptation to overfeaturize. Some features which are large and complex are
+flagged for conditional compilation.
+ Many people have contributed their time to developing, improving, and
+
+
+ 3
+
+
+
+
+
+
+
+porting MG. Mike Meyer, Mic Kaczmarczik, and Bob Larson deserve particular
+mention for their efforts.
+ Questions, suggestions, and offers of help should be addressed to:
+
+ mg-developers@ucbvax.berkeley.edu (ARPA)
+ ucbvax!mg-developers (UUCP)
+
+
+1.1 Implementations of MG
+
+MG runs on many different kinds of hardware under many different operating
+systems. Currently, these include:
+
+ - 4.2 and 4.3 BSD Unix (including Ultrix-32)
+
+ - System V Unix
+
+ - VAX/VMS
+
+ - Primos
+
+ - OS9/68k
+
+ - Amiga
+
+ - Atari ST
+
+ - MS-DOS
+
+
+ This document describes release MG2A. When we talk of different versions
+of MG in this manual, the term version is used to refer to the different
+support MG provides for the various machines and operating systems it runs
+under, not to different releases of MG itself. For example, we might speak
+of how the VMS version of MG differs from the Unix version.
+ As mentioned above, some MG commands may not be implemented in all
+versions; these are noted in the documentation. Some versions of MG also
+support features (such as mouse handling) that are not described here.
+
+1.2 A Note on Character Sets
+
+MG uses the 128-character ASCII character set, and provides support
+for 8-bit characters. Whether the particular version of MG that you
+are running knows about extended character sets depends on whether your
+
+
+ 4
+
+
+
+
+
+
+
+terminal and the host operating system know about them. Moreover, since
+there is no standard 8-bit character set, the same character codes will
+probably give different glyphs on different systems. Most versions of MG
+use the DEC multinational character set.
+
+
+1.3 Notation and Conventions
+
+In this manual, commands and other things that must be typed in literally
+are indicated in a typewriter font, like next-line. Placeholders such as
+command argument names use an italic font.
+ The terms command and function are synonymous. We often speak of a
+command being bound to a particular key, although you may actually have to
+type more than one character to form a single key. Most commands are bound
+to keys with control and meta modifiers.
+ To type a control character, use the control key on your keyboard like a
+shift key: hold down the control key while typing the character. In this
+manual, we will indicate control characters like C-x --- here, typing the
+character ``x'' while holding down the control key.
+ Some keyboards also have a meta key that works like the control key.
+(It may be labelled something else; on the Atari ST, for example, the key
+marked ``Alternate'' is the meta key.) If your keyboard doesn't have a
+meta key, don't panic. You can also use the escape key as a meta prefix;
+first type the escape, and then the character. Meta characters will be
+indicated as M-x.
+ Besides the meta prefix, two other characters are used as prefixes: C-x
+and C-h. A few keys have special notation: SPC is the space character,
+DEL is the delete or rubout character, RET is carriage return, and ESC
+is the escape character. NUL is the null character (ASCII 0), which is
+usually equivalent to either C-SPC or C-@.
+ Uppercase and lowercase characters are generally equivalent in command
+keystrokes.
+ When you run MG from a shell, command line arguments are interpreted as
+the names of files you want to visit, or edit. Each file is read into a
+buffer in memory. No changes are actually made to the file until you ask
+it to be written out to disk.
+ Within MG, the large top part of the screen serves as a window into
+the buffer being edited. Below this is the mode line, which displays the
+name of the buffer. Finally, at the very bottom of the screen, there is
+a one-line minibuffer which is used for displaying messages and answering
+questions.
+ MG keeps track of two pointers into each window, the point and the mark.
+The cursor appears at the point in the current window, and we often speak
+of moving the cursor rather than of moving the point. The text between the
+
+
+ 5
+
+
+
+
+
+
+
+point and the mark is referred to as the region.
+ Some commands deal with words and paragraphs. Generally, whitespace and
+punctuation separate words. Lines that are empty or that contain only
+spaces or tabs separate paragraphs without being part of a paragraph. A
+non-empty line that starts with a space or tab also begins a new paragraph.
+ A number of commands are defined as toggles. If no prefix argument is
+supplied, these commands toggle an action. The action is turned on if a
+negative or zero argument is supplied, and turned on if a positive argument
+is supplied.
+
+
+1.4 Getting Started
+
+This document is intended primarily as a reference manual. If you have
+never used any Emacs-like text editor before, it is strongly suggested that
+you run the on-line tutorial supplied with the MG distribution, instead of
+reading this manual.
+ Do not be put off by the large number of commands described in this
+manual! It is possible to get by with only a handful of basic commands.
+Here are the ones that are probably used most frequently:
+
+C-p Move the cursor to the previous line
+
+C-n Move the cursor to the next line
+
+C-b Move the cursor backwards
+
+C-f Move the cursor forwards
+
+C-v Scroll forwards one screenful
+
+M-v Scroll backwards one screenful
+
+M-< Go to the beginning of the buffer
+
+M-> Go to the end of the buffer
+
+C-a Go to the beginning of the line
+
+C-e Go to the end of the line
+
+DEL Delete the previous character
+
+C-k Kill (delete) to the end of line
+
+
+ 6
+
+
+
+
+
+
+
+C-y Reinsert killed text.
+
+C-x C-c Exit MG
+
+C-x C-s Save the current buffer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 7
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 2
+
+
+
+
+Using Commands
+
+
+
+2.1 Command Arguments
+
+Some commands require arguments. For example, if you want to read a file
+into a buffer, you must type in the name of the file. In the descriptions
+of commands in this manual, if arguments are required, they are listed
+following the command name.
+ MG prompts for command arguments in the minibuffer. Within the
+minibuffer, the following characters can be used for editing:
+
+
+DEL, C-h Erase the last character.
+
+C-x, C-u Erase the entire input line.
+
+C-w Erase to the beginning of the previous word.
+
+C-q, n Quote the next character typed.
+
+RET Signifies that you have completed typing in the argument.
+
+C-g Abort the command in progress.
+
+2.2 Prefix Arguments
+
+All commands accept an optional numeric prefix argument. This is often
+interpreted as a repetition count. For example, the function next-line,
+if given a prefix argument, will move the cursor forward that many lines;
+without an argument, it will move the cursor forward one line. A few
+commands behave differently if given a prefix argument than they do without
+
+
+ 8
+
+
+
+
+
+
+
+one, and others ignore the prefix argument entirely.
+
+digit-argument M-0, M-1, M-2, M-3, M-4, M-5, M-6, M-7, M-8, M-9
+negative-argument M--
+One way to specify a command argument is to use the escape key as a meta
+prefix, and then type one or more digits. A dash may be used for a
+negative argument.
+
+universal-argument C-u
+
+Another way to specify a command prefix is to type C-u. Typing one C-u is
+equivalent to a prefix argument of 4, typing two gives a value of 16, and
+so on. In addition, you can type digits following C-u to form a numeric
+prefix argument.
+
+2.3 Aborting
+
+
+keyboard-quit C-g
+Typing C-g cancels any command. It is particularly useful for cancelling a
+command when MG is prompting for input in the minibuffer.
+
+
+2.4 Extended Commands
+
+
+execute-extended-command command M-x
+Commands that are not bound to keys can be executed through execute
+extended-command. If a prefix argument is supplied, it is passed to the
+command being executed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 9
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 3
+
+
+
+
+Moving the Cursor
+
+
+
+The commands described in this chapter move the cursor (sometimes called
+the point or dot) within the current window. Commands which set the mark
+are included here as well.
+
+backward-char C-b
+Moves the cursor backward (left) one character. If the cursor is at the
+left margin, it will be moved to the end of the previous line.
+
+backward-paragraph M-[
+Moves the cursor backwards to the beginning of the current paragraph, or
+to the beginning of the previous paragraph if the cursor is already at the
+beginning of a paragraph.
+
+backward-word M-b
+
+Moves the cursor backwards to the beginning of the current word, or to the
+beginning of the previous word if the cursor is already at the beginning of
+a word.
+
+beginning-of-buffer M-<
+Moves the cursor backwards to the beginning of the buffer.
+
+beginning-of-line C-a
+Moves the cursor backwards to the beginning of the current line. This
+command has no effect if the cursor is already at the beginning of the
+line.
+
+end-of-buffer M->
+Moves the cursor forwards to the end of the buffer.
+
+
+ 10
+
+
+
+
+
+
+
+
+end-of-line C-e
+Moves the cursor forwards to the end of the current line. This command has
+no effect if the cursor is already at the end of the line.
+
+exchange-point-and-mark C-x C-x
+
+Set the mark at the current cursor position, and move the cursor to the old
+location of the mark.
+
+forward-char C-f
+Moves the cursor forwards one character. If the cursor is at the end of a
+line, it will be moved to the first character on the next line.
+
+forward-paragraph M-]
+Moves the cursor forwards to the next paragraph delimiter.
+
+forward-word M-f
+Moves the cursor forwards to the end of the current word, or to the end of
+the next word if the cursor is already at the end of a word.
+
+goto-line line-number
+Moves the cursor to the beginning of line line-number in the buffer.
+
+next-line C-n
+
+Moves the cursor down one line. The cursor remains in the same column
+unless it would be past the end of the line, in which case it is moved to
+the end of the line. At the end of the buffer, C-n will create new lines.
+
+previous-line C-p
+Moves the cursor up one line. The cursor remains in the same column unless
+it would be past the end of the line, in which case it is moved to the end
+of the line.
+
+recenter C-l
+Redraws the entire screen, scrolling the current window if necessary so
+that the cursor is near the center. With a positive prefix argument n, the
+window is scrolled so that the cursor is n lines from the top. A negative
+prefix argument puts the cursor that many lines from the bottom of the
+window.
+
+redraw-display
+
+
+
+ 11
+
+
+
+
+
+
+
+Redraws the entire screen, but never scrolls.
+
+scroll-down M-v
+Scrolls the display down (moving backward through the buffer). Without an
+argument, it scrolls slightly less than one windowful. A prefix argument
+scrolls that many lines.
+
+scroll-one-line-down
+scroll-one-line-up
+
+These functions are similar to scroll-down and scroll-up (respectively),
+but when invoked without an argument, cause the display to scroll by one
+line only. These functions are enabled by defining the compile-time option
+GOSMACS.
+
+scroll-other-window M-C-v
+Scrolls the ``other'' window forward as for scroll-up.
+
+scroll-up C-v
+Scrolls the display up (moving forward through the buffer). Without an an
+argument, it scrolls slightly less than one windowful. A prefix argument
+scrolls that many lines.
+
+set-mark-command NUL
+Set the mark at the current cursor position.
+
+what-cursor-position C-x =
+Prints some information in the minibuffer about where the cursor is.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 12
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 4
+
+
+
+
+Text Insertion Commands
+
+
+
+The usual way to insert text into a buffer is simply to type the
+characters. The default binding for all of the printing characters
+(self-insert-command) causes them to be inserted literally at the cursor
+position.
+
+insert string
+Insert string into the current buffer at the cursor position.
+
+newline RET
+Insert a line break into the current buffer at the cursor position, moving
+the cursor forward to the beginning of the new line.
+
+newline-and-indent C-j
+
+Insert a line break into the current buffer at the cursor position, then
+add extra whitespace so that the cursor is aligned in the same column as
+the first non-whitespace character in the previous line.
+
+open-line C-o
+Inserts a line break into the current buffer at the current position, but
+does not move the cursor forward.
+
+quoted-insert C-q
+This command acts as a prefix to cancel the normal interpretation of the
+next keystroke. If C-q is followed by one to three octal digits, it is
+interpreted as the code of the character to insert. Otherwise a single
+key is read and the character typed is inserted into the buffer instead
+of interpreted as a command. This is used for inserting literal control
+characters into a buffer.
+
+
+ 13
+
+
+
+
+
+
+
+
+self-insert-command
+This is the default binding for keys representing printable characters.
+The character is inserted into the buffer at the cursor position, and the
+cursor moved forward.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 14
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 5
+
+
+
+
+Killing, Deleting, and Moving Text
+
+
+
+When text is deleted, it is erased completely. Killing text, on the other
+hand, moves it into a temporary storage area called the kill buffer. The
+saved text in the kill buffer is erased when another block of text is
+killed. Until then, however, you can retrieve text from the kill buffer.
+This can be used to move or copy blocks of text, as well as to restore
+accidentally killed text.
+
+backward-kill-word M-DEL
+Kill the text backwards from the cursor position to the beginning of the
+current word. Typing M-DEL several times in succession prepends each
+killed word to the kill buffer.
+
+copy-region-as-kill M-w
+Copies the text in the region into the kill buffer, without removing it
+from the current buffer.
+
+delete-backward-char DEL
+
+Deletes the character to the left of the cursor.
+
+delete-blank-lines C-x C-o
+Deletes all blank lines after the current line, and if the current line is
+blank, deletes it and all blank lines preceeding it as well.
+
+delete-char C-d
+Deletes the character underneath the cursor.
+
+delete-horizontal-space M-n
+Deletes all spaces and tabs on either side of the cursor.
+
+
+ 15
+
+
+
+
+
+
+
+
+just-one-space M-SPC
+This is like delete-horizontal-space, except it leaves a single space at
+the cursor position.
+
+kill-line C-k
+
+If no prefix argument is specified, this function kills text up to the next
+newline; or if the cursor is at the end of a line, the newline is killed.
+A prefix argument specifies how many lines to kill. Typing C-k several
+times in succession appends each line to the kill buffer.
+
+kill-paragraph
+This command kills the entire paragraph containing the cursor. If the
+cursor is positioned between paragraphs, the next paragraph is killed.
+
+kill-region C-w
+The region (all text between point and mark) is killed.
+
+kill-word M-d
+Text is killed forward from the cursor position to the next end of word.
+If the cursor is at the end of the word, then the next word is killed.
+Typing M-d several times appends the killed text to the kill buffer.
+
+yank C-y
+Text is copied from the kill buffer into the current buffer at the cursor
+position. The cursor is moved to the end of the inserted text.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 16
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 6
+
+
+
+
+Searching and Replacing
+
+
+
+6.1 Searching
+
+The ordinary search command in MG differs from that in many other editors
+in that it is incremental: it begins searching as soon as you begin typing
+the search string, instead of waiting for you to type the entire string.
+ All of the search commands described in this section are
+case-insensitive.
+
+isearch-backward pattern C-r
+isearch-forward pattern C-s
+
+These commands perform an incremental search backward and forward
+(respectively) for pattern. MG will move the cursor to the place in the
+buffer that matches as much of the pattern as you have typed so far, as
+each character is entered.
+ Within the incremental search, the following characters are interpreted
+specially:
+
+DEL Erase the last character in the search string.
+
+ESC Stop searching; exit from incremental search mode, leaving
+ the cursor where the search brought it.
+
+C-g If a match has been found, exits from incremental search but
+ leaves the cursor in its original position. If the search
+ has failed, this will just erase the characters which have
+ not been found from the end of the search pattern. In this
+ case, you must type C-g again to abort the search.
+
+
+
+ 17
+
+
+
+
+
+
+
+C-s Search forward for the next occurrence of the same pattern.
+
+C-r Search backward for the previous occurrence of the same
+ pattern.
+
+C-q ``Quotes'' the next character typed, forcing it to be
+ interpreted as a literal character in the search pattern.
+
+ In addition, normal commands such as C-a that do not have special
+meanings within incremental search cause the search to be terminated, and
+then are executed in the ordinary way.
+
+search-again
+search-backward pattern M-r
+search-forward pattern M-s
+
+These commands perform ordinary, non-incremental searches. Search-again
+uses the same pattern and direction as the previous search.
+
+6.2 Replacing
+
+
+query-replace pattern replacement M-%
+The primary replace command in MG is an interactive query replace. MG
+searches forward for occurrences of pattern, and asks you what to do about
+each one. The choices are:
+
+
+SPC Replace this match with replacement, and go on to the next.
+
+DEL Skip to the next match without replacing this one.
+
+. Replace this match, and then quit.
+
+! Replace all remaining occurrences without asking again.
+
+ESC Quit.
+
+ By default, query-replace adjusts the case of lower-case letters in
+the replacement string to match that of the particular occurrence of the
+pattern; for example, replacing ``Foo'' with ``bar'' results in ``Bar''.
+Upper case letters in the replacement string are always left uppercase. In
+addition, supplying a prefix argument will also tell query-replace to leave
+the case of the replacement string as-is.
+
+
+ 18
+
+
+
+
+
+
+
+ Note that query-replace always performs a case-insensitive search.
+
+
+6.3 Regular Expressions
+
+Regular expressions provide a means for specifying complex search patterns,
+instead of just a literal string. The commands in this section are
+available only if MG is compiled with the REGEX option defined.
+ Regular expression syntax uses the following rules. Most characters
+in a regular expression are considered to be ordinary characters, and
+will match themselves and nothing else. The exceptions are the special
+characters listed below.
+
+. Matches any single character except a newline.
+
+* A suffix operator that matches zero or more repetitions of
+ the (smallest) preceding regular expression.
+
++ A suffix operator that matches one or more repetitions of
+ the (smallest) preceding regular expression.
+
+? A suffix operator that matches either zero or one occurence
+ of the (smallest) preceding regular expression.
+
+[... ] Matches any one character listed in the character set
+ between the square brackets. See examples below.
+
+^ Matches at the beginning of a line.
+
+$ Matches at the end of a line.
+
+n Except for the situations listed below, acts as a prefix
+ operator which causes the character following to be treated
+ as an ordinary character.
+
+n| An infix binary or operator. It applies to the two largest
+ surrounding expressions.
+
+n(... n) A grouping construct, usually used to specify a larger
+ expression for postfix operators such as * or to limit the
+ scope of operands to \|.
+
+ndigit Matches the same text matched by the digitth \(...\)
+ construct. These are numbered from 1 to 9 in the order that
+
+
+ 19
+
+
+
+
+
+
+
+ the open-parentheses appear.
+
+n` Matches at the beginning of the buffer.
+
+n' Matches at the end of the buffer.
+
+nb Matches at the beginning or end of a word.
+
+nB Matches anyplace except at the beginning or end of a word.
+
+n< Matches at the beginning of a word.
+
+n> Matches at the end of a word.
+
+nw Matches any word-constituent character.
+
+nW Matches any character which is not a word-constituent.
+
+ Some examples may help clarify the rules.
+
+
+foo Matches the literal string foo.
+
+;.* Matches all strings which begin with a semicolon and
+ continue to the end of a line.
+
+c[ad]+r Matches strings of the form car, cdr, caar, cadr, and so on.
+
+[a-z] Matches any lowercase letter.
+
+[^a-z] Matches any character except lowercase letters.
+
+[0-9+---] Matches a digit or sign.
+
+n(foon|barn) Matches either the string foo or the string bar.
+
+
+count-matches pattern
+count-non-matches pattern
+These commands count the number of lines which do or do not (respectively)
+match the specified pattern.
+
+delete-matching-lines pattern
+delete-non-matching-lines pattern
+
+
+ 20
+
+
+
+
+
+
+
+These commands delete all lines which do or do not (respectively) match the
+specified pattern.
+
+query-replace-regexp pattern replacement
+This is the regular expression version of query-replace.
+ The replacement string may be a constant, or it can refer to all or
+part of the string matched by the pattern. \& in the replacement string
+expands into the entire text being replaced, while \n (where n is a number)
+replaces the nth parenthesized expression in pattern.
+
+re-search-again
+re-search-backward pattern
+re-search-forward pattern
+
+These are the regular expression equivalents of the ordinary non-
+incremental search commands.
+
+set-case-fold-search
+This command toggles an internal variable that controls whether the regular
+expression search and replace commands pay attention to case. By default,
+regular expression searches are case-insensitive. Ordinary searches are
+always case-insensitive and are not affected by the setting of this
+variable.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 21
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 7
+
+
+
+
+Windows
+
+
+
+MG initially has only one text window displayed. However, you can have as
+many windows as will fit on the screen. Each window has its own mode line
+and must display at least two lines of text. (Note that a MG's ``windows''
+are distinct from the ``windows'' handled by screen managers such as the X
+Window System.)
+ Multiple windows may be used to display different buffers. You can also
+have the same buffer displayed in more than one window, which is useful if
+you want to see one part of a file at the same time as you are editing
+another part.
+ Although many windows can be displayed at once, only one window is
+active at any given time. This is the window where the cursor appears.
+ Some commands refer to the ``other'' window. This is the window
+directly below the current window, or the top window if you are in the
+bottom window.
+
+delete-other-windows C-x 1
+Makes the current window the only window.
+
+delete-window C-x 0
+Deletes the current window, making the ``other'' window the current window.
+This command doesn't do anything useful if there is only one window being
+displayed.
+
+enlarge-window C-^
+
+Makes the current window larger. Without a prefix argument, the window
+grows one line; otherwise, the prefix argument specifies how many lines to
+grow.
+
+
+
+ 22
+
+
+
+
+
+
+
+other-window C-x o
+Makes the ``other'' window the current window.
+
+previous-window
+
+This is like other-window, except that it cycles through the windows in
+reverse order. This command is available only if MG was compiled with the
+GOSMACS option defined.
+
+shrink-window
+Makes the current window smaller. Without a prefix argument, the window
+loses one line; otherwise, the prefix argument specifies how many lines go
+away.
+
+split-window-vertically C-x 2
+Split the current window into two windows, both using the same buffer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 23
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 8
+
+
+
+
+Files and Buffers
+
+
+
+Most buffers are used to contain a file being edited. It is also possible
+to have buffers that are not associated with any file; MG uses these for
+purposes such as displaying help text, for example. However, since most
+commands for dealing with files also deal with buffers, we have grouped all
+of these commands together into one chapter.
+
+8.1 Buffer Manipulation
+
+
+insert-buffer buffer-name
+
+Inserts the contents of the named buffer into the current buffer at the
+cursor location. The cursor moves to the end of the inserted text.
+
+kill-buffer buffer-name C-x k
+The named buffer and its contents are deleted. If the buffer has been
+marked as modified, MG will ask you if you really want to delete it. Note
+that, contrary to its name, this command does not save the buffer contents
+in the kill buffer.
+ If a buffer is being displayed in a window when it is deleted, MG will
+find some other buffer to display in the same window.
+
+list-buffers C-x C-b
+This command writes information about the buffers currently in use to a
+buffer named *Buffer List*. This buffer is then displayed in the ``other''
+window; if there is only one window, this command will split the screen
+into two windows.
+
+not-modified M-~
+
+
+ 24
+
+
+
+
+
+
+
+This command makes MG think that the current buffer has not been modified,
+even if it really has been changed. This affects the behavior of the
+kill-buffer and the buffer-saving commands described below.
+ MG indicates modified buffers with two stars at the left end of the mode
+line.
+
+switch-to-buffer buffer-name C-x b
+The current window is mapped onto the named buffer. If there isn't already
+a buffer with that name around, MG will create one.
+
+switch-to-buffer-other-window buffer-name C-x 4 b
+
+This command works like switch-to-buffer, except that the ``other'' window
+is used. If there is only one window, this command splits the screen into
+two windows and maps the named buffer onto one of them.
+
+8.2 Reading and Writing Files
+
+
+find-file file-name C-x f
+find-file-other-window file-name C-x 4 C-f
+These commands are analagous to switch-to-buffer and switch-to-buffer-
+other-window, respectively. The difference is that these commands look for
+a buffer associated with the named file. If no matching buffer is found,
+MG will create a new buffer with a name derived from the filename, and
+attempt to read the file into the buffer. If the named file cannot be
+opened, the buffer remains empty.
+
+insert-file file-name C-x i
+
+This command reads in the contents of the named file into the current
+buffer at the cursor position. The cursor remains in the same place.
+
+save-buffer C-x C-s
+If the current buffer has been modified, it is saved. Buffers that are not
+associated with files cannot be written out with this command.
+
+save-buffers-kill-emacs C-x C-c
+This command is used to leave MG and return control to the shell or other
+program that was used to start MG. If there are modified buffers, MG will
+ask you if you want to save them before exiting.
+
+save-some-buffers C-x s
+MG will ask you if you want to save modified buffers that are associated
+
+
+ 25
+
+
+
+
+
+
+
+with files.
+
+write-file file-name C-x C-w
+The current buffer is written out using the file name supplied. This
+is useful for saving buffers that are not associated with files, or for
+writing out a file with a different name than what was used to read it in.
+
+
+8.3 Backup Files
+
+MG provides a way to save a copy of the original version of files which
+have been modified and then written out again. The backup copy reflects
+the state of the file as it existed the first time it was read into MG. The
+name used for the backup file varies, depending on the operating system.
+ This feature is disabled if MG is compiled with NOBACKUP defined.
+
+make-backup-files
+This command is a toggle which controls the state of an internal variable
+that determines whether MG creates backup files.
+
+
+8.4 Changing the Directory
+
+The commands in this section are disabled by defining NODIR.
+
+cd directory-name
+This command changes MG's notion of the ``current'' directory or pathname.
+This is used to supply defaults for functions that read or write files.
+ The syntax for directory-name is obviously specific to the particular
+operating system MG is running on.
+
+pwd
+Display what MG thinks is the current directory.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 26
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 9
+
+
+
+
+Modes
+
+
+
+Modes are used to locally alter the bindings of keys on a buffer-by-buffer
+basis. MG is normally in fundamental mode, and these are the bindings that
+are listed with the command descriptions in this manual. Modes define
+additional keymaps that are searched for bindings before the fundamental
+mode bindings are examined; see the section on key binding below for more
+details on how this works.
+
+set-default-mode mode-name
+Normally, when MG visits a file, it puts the associated buffer into
+fundamental mode. Using the set-default-mode command, you can specify that
+MG should default to use some other mode on all subsequent buffers that are
+created. This command is a toggle. With no prefix argument, if the named
+mode is not already on the list of default modes, then it will be added to
+the list; otherwise, it is removed from the list.
+
+
+9.1 No Tab Mode
+
+In notab mode, tabs are expanded into spaces instead of inserted literally
+into the buffer. Literal tab characters are displayed as ^I (much like
+other control characters). These commands are available if MG is compiled
+with the symbol NOTAB defined. (This mode is mainly for use on systems
+such as PRIMOS that do not treat tab as a series of spaces.)
+
+no-tab-mode
+This command is a toggle to control whether notab mode is in effect.
+
+space-to-tabstop
+Insert enough spaces to move the cursor to the next tab stop. In notab
+
+
+ 27
+
+
+
+
+
+
+
+mode, this function is bound to C-i.
+
+
+9.2 Overwrite Mode
+
+Normally, when characters are inserted into the buffer, they are spliced
+into the existing text. In overwrite mode, inserting a character causes
+the character already at the cursor position to be replaced. This is
+useful for editing pictures, tables, and the like.
+
+overwrite-mode
+This command is a toggle which controls whether overwrite mode is in
+effect.
+
+9.3 Auto Fill
+
+Fill mode causes newlines to be added automatically at word breaks when
+text is added at the end of a line, extending past the right margin. Auto
+fill is useful for editing text and documentation files.
+
+auto-fill-mode
+
+This command is a toggle which controls whether fill mode is in effect.
+
+insert-with-wrap
+This command works like self-insert, except that it checks to see if the
+cursor has passed the right margin. If so, it fills the line by inserting
+a line break between words. This command is bound to SPC in fill mode.
+
+fill-paragraph M-q
+Fill the paragraph containing the cursor.
+
+set-fill-column C-x f
+Without a prefix argument, this command sets the right margin at the
+current cursor column. If a prefix argument is supplied, it is used
+instead as the line width.
+
+
+9.4 Auto Indent
+
+Indent mode binds RET to newline-and-indent, so that each new line is
+indented to the same level as the preceeding line. This mode is useful for
+editing code.
+
+
+
+ 28
+
+
+
+
+
+
+
+auto-indent-mode
+This command is a toggle which controls whether auto-indent mode is in
+effect.
+
+
+9.5 Blink
+
+Blink mode makes it easier to match parentheses, brackets, and other
+paired delimiters. When the closing delimiter is typed, the cursor moves
+momentarily to the matching opening delimiter (if it is on the screen), or
+displays the line containing the matching delimiter on the echo line. This
+is useful for editing Lisp or C code, or for preparing input files for text
+processors such as LaTeX that use paired delimiters.
+
+blink-matching-paren
+This command is a toggle which controls whether blink mode is in effect.
+
+blink-matching-paren-hack
+This function behaves like self-insert, except that it finds the matching
+delimiter as described above. In blink mode, this function is bound to ),
+which flashes the matching (. This function also knows about the pairs {},
+[], and <>. All other characters match with themselves.
+
+
+9.6 Dired Mode
+
+``Dired'' is an abbreviation for ``directory editor'', and it provides a
+way to browse through the contents of a directory from with MG. Dired puts
+a directory listing into a buffer; you can use normal editing commands to
+move around the buffer, and a special group of commands to manipulate the
+files. For example, there are commands to delete and rename files, and to
+read a file into an MG buffer.
+ Since dired mode rebinds many keys, a table may be helpful:
+
+ C-d dired-flag-file-deleted
+ SPC next-line
+ c dired-copy-file
+ d dired-flag-file-deleted
+ e dired-find-file
+ f dired-find-file
+ n next-line
+ o dired-find-file-other-window
+ p previous-line
+ r dired-renamefile
+
+
+ 29
+
+
+
+
+
+
+
+ u dired-unflag
+ x dired-do-deletions
+ DEL dired-backup-unflag
+
+ The commands in this section are disabled by defining NODIRED.
+
+dired directory-name C-x d
+Creates a dired buffer for the given directory name, and displays it in
+the current window. The files in the directory are listed, usually along
+with information about the file such as its size and timestamp. The exact
+format of the information is system-specific.
+
+dired-backup-unflag
+
+This function removes the deletion flag from the file listed on the
+previous line of the dired buffer.
+
+dired-copy-file new-name
+Copy the file listed on the current line of the dired buffer.
+
+dired-do-deletions
+Deletes the files that have been flagged for deletion.
+
+dired-find-file
+dired-find-file-other-window
+These function works like find-file and find-file-other-window, except that
+the filename is taken from the current line in the dired buffer.
+
+dired-flag-file-deleted
+
+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 not
+actually deleted until the function dired-do-deletions is executed.
+
+dired-other-window directory-name
+This function works just like dired, except that it puts the dired buffer
+in the ``other'' window.
+
+dired-rename-file new-name
+Renames the file listed on the current line of the dired buffer. Note that
+the dired buffer is not updated to reflect the change.
+
+dired-unflag
+Remove the deletion flag for the file on the current line.
+
+
+ 30
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 10
+
+
+
+
+Miscellaneous
+
+
+
+10.1 Help
+
+Most of the commands in this section write useful information to the *help*
+buffer, which is then displayed in the ``other'' window.
+ These commands can be disabled at compile-time by defining NOHELP.
+
+apropos topic C-h a
+
+This command lists all functions whose names contain a string matching
+topic in the *help* buffer.
+
+describe-bindings C-h b
+Information about the key bindings in effect in the current buffer is
+listed in the *help* buffer.
+
+describe-key-briefly key C-h c
+Information about the binding of key is printed in the minibuffer.
+
+help-help option C-h C-h
+This command lists all of the help options available and prompts for which
+one to run. Currently, these include only a to run apropos, b to run
+describe-bindings, and c to run describe-key-briefly.
+
+
+10.2 Keyboard Macros
+
+A keyboard macro is a saved set of commands from the keyboard that can be
+reexecuted later on. There can only be one keyboard macro defined at any
+one time.
+
+
+ 31
+
+
+
+
+
+
+
+ The commands in this section are available unless they have been
+disabled by defining NOMACRO.
+
+call-last-kbd-macro C-x e
+Execute the saved keyboard macro. A prefix argument can be used to specify
+a repetition count.
+
+end-kbd-macro C-x )
+start-kbd-macro C-x (
+
+These functions are used to define a keyboard macro. All keys entered
+after start-kbd-macro is executed, up to a end-kbd-macro, are remembered as
+they are executed. You can then reexecute the same sequence of operations
+using call-last-kbd-macro.
+
+10.3 Changing Case
+
+MG provides a number of functions for changing the case of text.
+
+capitalize-word M-c
+downcase-region C-x C-l
+downcase-word M-l
+upcase-region C-x C-u
+upcase-word M-u
+All of these commands do the obvious.
+
+
+10.4 Odds and Ends
+
+This section describes miscellaneous commands that don't fit into any
+particular category.
+
+emacs-version
+Prints information about the version of MG you are running in the
+minibuffer.
+
+meta-key-mode
+If the particular version of MG you are running supports a meta key, this
+function can be used to determine whether MG actually pays attention to it
+or not. If no prefix argument is supplied, the internal variable that
+controls the use of the meta key is toggled; a positive value enables the
+meta key, while a negative value disables it.
+
+
+
+
+ 32
+
+
+
+
+
+
+
+prefix-region
+set-prefix-string string
+Prefix-region is used to prefix each line of the region with a string.
+This is useful for indenting quoted text, making block comments, and the
+like. The function set-prefix-string can be used to set the string used as
+the prefix.
+
+suspend-emacs C-z
+
+This command temporarily suspends MG so that you can run other programs,
+and later resume editing. The exact behavior depends on which operating
+system you are running MG under. Typically, MG will either spawn a new
+shell as a subprocess, or return you to the parent process.
+
+transpose-chars C-t
+This command transposes the previous two characters.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 33
+
+
+
+
+
+
+
+
+
+
+
+
+
+Chapter 11
+
+
+
+
+Customization
+
+
+
+MG provides a limited support for customization. However, unlike ``real''
+Emacs, there is no extension language for interpretively defining new
+functions.
+
+11.1 Key Bindings
+
+MG allows keys to be rebound locally or globally. To understand the
+difference between the two, some discussion on how modes are implemented is
+necessary.
+ An internal data structure called a keymap is used to look up the
+function that is bound to a particular key. The keymap for fundamental
+mode contains all of the default bindings which are listed with the command
+descriptions in this manual. Modes define additional keymaps that are
+searched for a binding before the fundamental mode keymap is examined.
+Keymaps have the same name as the mode they are associated with.
+ MG does not provide commands for defining new modes, but you can alter
+the keymaps for existing modes.
+
+define-key keymap-name key command
+
+This command can be used to modify the keymap for the named mode.
+
+global-set-key key command
+global-unset-key key
+These commands modify the keymap for fundamental mode. Bindings
+established by global-set-key will be inherited by all other modes, as long
+as they do not establish local rebindings of the same key.
+
+local-set-key key command
+
+
+ 34
+
+
+
+
+
+
+
+local-unset-key key
+These commands modify the keymap currently in effect.
+
+
+11.2 Startup Files
+
+Although MG does not include a general-purpose extension language, it does
+provide a way to read and evaluate commands using a somewhat different
+syntax than that used for executing extended commands. This is typically
+used in a startup file to modify key bindings.
+ A startup file consists of one or more expressions. Each expression
+must appear on a separate line in the file; there may not be more than
+one expression per line, nor may expressions span across line breaks.
+Whitespace (spaces and tabs) separate the tokens in an expression. For
+historical reasons, parentheses are also considered to be whitespace in
+this context. A semicolon acts as a comment character, causing the rest of
+the line to be discarded.
+ An expression consists of a function name, an optional prefix argument
+(given as an integer constant), and arguments to be passed to the function.
+If an argument includes literal whitespace or nonprintable characters (for
+example, as in a keystroke argument to one of the key binding functions
+described in the previous section), it must be supplied as a string
+constant enclosed in double quotes.
+ Within string constants, the following backslash escapes are available
+to specify nonprintable characters:
+
+nt, nT Tab
+
+nn, nN Newline
+
+nr, nR Carriage return
+
+ne, nE Escape (Meta prefix)
+
+n^ Control prefix
+
+nn Specifies a character by its ASCII code, where n may consist
+ of from one to three octal digits
+
+nfn, nFn Specifies the keycode for the nth function key. N may
+ consist of one or two decimal digits.
+
+ The following commands which deal with evaluation of expressions are
+disabled by defining the compile-time option NOSTARTUP.
+
+
+ 35
+
+
+
+
+
+
+
+ The Rutgers Sun version will attempt to read two different startup
+files, a general startup file and a terminal-specific startup file.
+The terminal-specific startup file is intended primarily to define the
+keypad. The general startup file is .mg in your home directory.
+If there is no such file, /usr/local/lib/mg/mg will be used. The
+terminal-specific startup file is .mg-TYPE, where TYPE represents the name
+of the terminal type. E.g if your terminal type is set to vt100, MG
+will read a file .mg-vt100. If there is no such file, it will try
+/usr/local/lib/mg/mg-vt100. Files should exist in /usr/local/lib/mg for
+the terminal types commonly in use at Rutgers.
+ For other versions, see the implementation notes for your particular
+version of MG for information on how it handles startup files.
+
+eval-current-buffer
+Evaluate the expressions in the current buffer.
+
+eval-expression expression
+
+Evaluate the expression supplied.
+
+load file-name
+Read in the specified file and evaluate its contents.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 36
+
+
+
+
+
+
+
+
+Fundamental Mode Key Bindings
+NUL set-mark-command C-x C-o delete-blank-lines
+C-a beginning-of-line C-x C-s save-buffer
+C-b backward-char C-x C-u upcase-region
+C-d delete-char C-x C-w write-file
+C-e end-of-line C-x C-x exchange-point-and-mark
+C-f forward-char C-x ( start-kbd-macro
+C-g keyboard-quit C-x ) end-kbd-macro
+C-h help C-x 0 delete-window
+TAB self-insert-command C-x 1 delete-other-windows
+C-j newline-and-indent C-x 2 split-window-vertically
+C-k kill-line C-x 4 c-x 4 prefix
+C-l recenter C-x = what-cursor-position
+RET newline C-x ^ enlarge-window
+C-n next-line C-x b switch-to-buffer
+C-o open-line C-x d dired
+C-p previous-line C-x e call-last-kbd-macro
+C-q quoted-insert C-x f set-fill-column
+C-r isearch-backward C-x i insert-file
+C-s isearch-forward C-x k kill-buffer
+C-t transpose-chars C-x o other-window
+C-u universal-argument C-x s save-some-buffers
+C-v scroll-up C-x 4 C-f find-file-other-window
+C-w kill-region C-x 4 C-g keyboard-quit
+C-x c-x prefix C-x 4 b switch-to-buffer-other-
+C-y yank window
+C-z suspend-emacs C-x 4 f find-file-other-window
+ESC meta prefix
+SPC .. ~ self-insert-command M-C-g keyboard-quit
+DEL delete-backward-char M-C-v scroll-other-window
+ M-SPC just-one-space
+C-h C-g keyboard-quit M-% query-replace
+C-h C-h help-help M-- negative-argument
+C-h a apropos M-0 digit-argument
+C-h b describe-bindings M-1 digit-argument
+C-h c describe-key-briefly M-2 digit-argument
+ M-3 digit-argument
+C-x C-b list-buffers M-4 digit-argument
+C-x C-c save-buffers-kill-emacs M-5 digit-argument
+C-x C-f find-file M-6 digit-argument
+C-x C-g keyboard-quit M-7 digit-argument
+C-x C-l downcase-region M-8 digit-argument
+
+
+
+ 37
+
+
+
+
+
+
+
+M-9 digit-argument
+M-< beginning-of-buffer
+M-> end-of-buffer
+M-[ backward-paragraph
+M-\ delete-horizontal-space
+M-] forward-paragraph
+M-b backward-word
+M-c capitalize-word
+M-d kill-word
+M-f forward-word
+M-l downcase-word
+M-q fill-paragraph
+M-r search-backward
+M-s search-forward
+M-u upcase-word
+M-v scroll-down
+M-w copy-region-as-kill
+M-x execute-extended-command
+M-~ not-modified
+M-DEL backward-kill-word
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 38
+
+
+
+
+
+
+
+
+
+
+
+Index
+
+
+
+apropos ....................... 31 dired-other-window ............ 30
+auto-fill-mode ................ 27 dired-rename-file ............. 30
+auto-indent-mode .............. 28 dired-unflag .................. 30
+backward-char .................. 9 downcase-region ............... 32
+backward-kill-word ............ 14 downcase-word ................. 32
+backward-paragraph ............. 9 emacs-version ................. 32
+backward-word .................. 9 end-kbd-macro ................. 32
+beginning-of-buffer ............ 9 end-of-buffer .................. 9
+beginning-of-line .............. 9 end-of-line ................... 10
+blink-matching-paren .......... 28 enlarge-window ................ 21
+blink-matching-paren-hack ..... 28 eval-current-buffer ........... 36
+call-last-kbd-macro ........... 32 eval-expression ............... 36
+capitalize-word ............... 32 exchange-point-and-mark ....... 10
+cd ............................ 25 execute-extended-command ....... 8
+copy-region-as-kill ........... 14 fill-paragraph ................ 27
+count-matches ................. 19 find-file ..................... 24
+count-non-matches ............. 19 find-file-other-window ........ 24
+define-key .................... 34 forward-char .................. 10
+delete-backward-char .......... 14 forward-paragraph ............. 10
+delete-blank-lines ............ 14 forward-word .................. 10
+delete-char ................... 14 global-set-key ................ 34
+delete-horizontal-space ....... 15 global-unset-key .............. 34
+delete-matching-lines ......... 19 goto-line ..................... 10
+delete-non-matching-lines ..... 19 help-help ..................... 31
+delete-other-windows .......... 21 insert ........................ 12
+delete-window ................. 21 insert-buffer ................. 23
+describe-bindings ............. 31 insert-file ................... 24
+describe-key-briefly .......... 31 insert-with-wrap .............. 27
+digit-argument ................. 8 isearch-backward .............. 16
+dired ......................... 29 isearch-forward ............... 16
+dired-backup-unflag ........... 29 just-one-space ................ 15
+dired-copy-file ............... 29 keyboard-quit .................. 8
+dired-do-deletions ............ 29 kill-buffer ................... 23
+dired-find-file ............... 29 kill-line ..................... 15
+dired-find-file-other-window .. 29 kill-paragraph ................ 15
+dired-flag-file-deleted ....... 29 kill-region ................... 15
+
+
+ 39
+
+
+
+
+
+
+
+kill-word ..................... 15 set-prefix-string ............. 33
+list-buffers .................. 23 shrink-window ................. 22
+load .......................... 36 space-to-tabstop .............. 26
+local-set-key ................. 34 split-window-vertically ....... 22
+local-unset-key ............... 35 start-kbd-macro ............... 32
+make-backup-files ............. 25 suspend-emacs ................. 33
+meta-key-mode ................. 32 switch-to-buffer .............. 24
+negative-argument .............. 8 switch-to-buffer-other-window . 24
+newline ....................... 12 transpose-chars ............... 33
+newline-and-indent ............ 12 universal-argument ............. 8
+next-line ..................... 10 upcase-region ................. 32
+no-tab-mode ................... 26 upcase-word ................... 32
+not-modified .................. 24 what-cursor-position .......... 11
+open-line ..................... 12 write-file .................... 25
+other-window .................. 21 yank ......................... 15
+overwrite-mode ................ 27
+prefix-region ................. 33
+previous-line ................. 10
+previous-window ............... 22
+pwd ........................... 25
+query-replace ................. 17
+query-replace-regexp .......... 19
+quoted-insert ................. 12
+re-search-again ............... 20
+re-search-backward ............ 20
+re-search-forward ............. 20
+recenter ...................... 10
+redraw-display ................ 11
+save-buffer ................... 24
+save-buffers-kill-emacs ....... 24
+save-some-buffers ............. 25
+scroll-down ................... 11
+scroll-one-line-down .......... 11
+scroll-one-line-up ............ 11
+scroll-other-window ........... 11
+scroll-up ..................... 11
+search-again .................. 17
+search-backward ............... 17
+search-forward ................ 17
+self-insert-command ........... 13
+set-case-fold-search .......... 20
+set-default-mode .............. 26
+set-fill-column ............... 27
+set-mark-command .............. 11
+
+
+ 40
+ \ No newline at end of file
diff --git a/usr.bin/mg/mg.dvi b/usr.bin/mg/mg.dvi
new file mode 100644
index 00000000000..427c39c48ea
--- /dev/null
+++ b/usr.bin/mg/mg.dvi
Binary files differ
diff --git a/usr.bin/mg/mg.tex b/usr.bin/mg/mg.tex
new file mode 100644
index 00000000000..75159922c4d
--- /dev/null
+++ b/usr.bin/mg/mg.tex
@@ -0,0 +1,1345 @@
+%\batchmode %be nice to fellow VAXers
+\hoffset=0in \voffset=0in %correct dvi2ln3 bug
+\documentstyle[11pt,titlepage,dvidoc]{report}
+%\pagestyle{headings} %not sure on this one yet
+%\parindent=0in
+\textwidth=5.5in
+%\oddsidemargin=0.25in
+
+% definitions for DEFINE environment
+\newlength{\lblwide}
+\newcommand{\resetmarg}{\settowidth{\leftmargin}{\makebox[\lblwide]{}\makebox[\labelsep]{}}}
+\newcommand{\resetlbl}{\setlength{\labelwidth}{\lblwide}}
+\newenvironment{define}[1]{\begin{list}{}{\settowidth{\lblwide}{\bf #1}\resetmarg \resetlbl}}{\end{list}}
+
+% definitions for fn environment
+
+\newcommand{\fname}[3]{\hspace{1em}\\{\tt #1}\index{#1} {\it #2}\hfill{\tt #3}}
+\newcommand{\fmore}[3]{\\{\tt #1}\index{#1} {\it #2}\hfill{\tt #3}}
+\newcommand{\fbody}{\\[0.5em]}
+
+% definitions for making the index
+\newcommand{\indexentry}[2]{\item {\tt #1} \dotfill\ #2}
+
+
+\setcounter{secnumdepth}{5} %personal preference
+\setcounter{tocdepth}{5} %personal preference
+\author{Sandra J. Loosemore}
+\title{The MG Reference Manual \\ Release MG2A}
+\date{\vspace{1.5in}
+Copyright \copyright 1987, Sandra J. Loosemore \\[0.5em]
+\parbox{5 in}{This document, or sections of this document, may be freely
+redistributed provided that the copyright notice and the following disclaimer
+remain intact: The author bears no responsibilities for errors in
+this document or the software it describes; and shall not be held liable
+for any indirect, incidental, or consequential damages.}}
+
+\makeindex %usually not
+
+\begin{document}
+\maketitle
+
+\tableofcontents
+
+\chapter{Introduction}
+
+MG is a small, fast, and portable Emacs-style text editor intended to
+be used by people who can't run a real Emacs for one reason or another
+--- as their main editor on smaller machines with limited memory or
+file space, or as a ``quick-start'' editor on larger systems, useful
+for composing short mail messages and the like.
+
+We've made MG compatible with GNU Emacs because that is the ``big'',
+full-featured editor that many of us use regularly and are most
+familiar with. GNU Emacs is the creation of Richard M. Stallman, who
+was also the author of the original Emacs editor. However, MG is not
+associated in any way with the GNU project, and the MG authors
+individually may or may not agree with the opinions expressed by
+Richard Stallman and the GNU project.
+
+MG is largely public domain. You can use, modify, and redistribute MG
+as you like. A few modules, however, are copyrighted; specifically,
+the regular expression code, the VMS termcap routines, and the Amiga
+support code. Look at the source code for the exact copyright
+restrictions.
+
+There are several other editors in existence which call themselves
+MicroEmacs. The original public domain version was written by Dave
+Conroy and circulated as version 1.6. Derived from this, there is
+another PD version by Dave Conroy numbered v30; a significantly larger
+PD version by Daniel Lawrence which is now up to version 3.9; at least
+one proprietary implementation; an implementation for the Atari ST
+with an integrated command shell, by Prabhaker Mateti; and probably
+others that we don't know about.
+
+MG is derived from the v30 MicroEmacs, with key bindings, command
+names, and general functionality made more compatible with GNU Emacs.
+Like v30, MG is fairly small and quite robust. We have generally
+resisted the temptation to overfeaturize. Some features which are
+large and complex are flagged for conditional compilation.
+
+Many people have contributed their time to developing, improving, and
+porting MG. Mike Meyer, Mic Kaczmarczik, and Bob Larson deserve
+particular mention for their efforts.
+
+Questions, suggestions, and offers of help should be addressed to:
+
+\begin{verbatim}
+ mg-developers@ucbvax.berkeley.edu (ARPA)
+ ucbvax!mg-developers (UUCP)
+\end{verbatim}
+
+\section{Implementations of MG}
+
+MG runs on many different kinds of hardware under many different
+operating systems. Currently, these include:
+
+\begin{itemize}
+
+\item 4.2 and 4.3 BSD Unix (including Ultrix-32)
+\item System V Unix
+\item VAX/VMS
+\item Primos
+\item OS9/68k
+\item Amiga
+\item Atari ST
+\item MS-DOS
+
+\end{itemize}
+
+This document describes release MG2A. When we talk of different
+versions of MG in this manual, the term {\em version\/} is used to
+refer to the different support MG provides for the various machines
+and operating systems it runs under, not to different releases of MG
+itself. For example, we might speak of how the VMS version of MG
+differs from the Unix version.
+
+As mentioned above, some MG commands may not be implemented in all
+versions; these are noted in the documentation. Some versions of MG
+also support features (such as mouse handling) that are not described
+here.
+
+\section{A Note on Character Sets}
+
+MG uses the 128-character ASCII character set, and provides support for
+8-bit characters. Whether the particular version of MG that you are running
+knows about extended character sets depends on whether your terminal and
+the host operating system know about them. Moreover, since there is no
+standard 8-bit character set, the same character codes will probably
+give different glyphs on different systems. Most versions of MG use
+the DEC multinational character set.
+
+\section{Notation and Conventions}
+
+In this manual, commands and other things that must be typed in
+literally are indicated in a typewriter font, like {\tt next-line}.
+Placeholders such as command argument names use an italic font.
+
+The terms {\em command\/} and {\em function\/} are synonymous. We often
+speak of a command being bound to a particular {\em key\/}, although you
+may actually have to type more than one character to form a single key.
+Most commands are bound to keys with {\em control\/} and {\em meta\/}
+modifiers.
+
+To type a control character, use the control key on your
+keyboard like a shift key: hold down the control key while typing the
+character. In this manual, we will indicate control characters like
+\verb"C-x" --- here, typing the character ``x'' while holding down
+the control key.
+
+Some keyboards also have a meta key that works like the control
+key. (It may be labelled something else; on the Atari ST, for example,
+the key marked ``Alternate'' is the meta key.) If your keyboard doesn't
+have a meta key, don't panic. You can also use the escape key as a meta
+prefix; first type the escape, and {\em then\/} the character. Meta
+characters will be indicated as \verb"M-x".
+
+Besides the meta prefix, two other characters are used as prefixes:
+\verb"C-x" and \verb"C-h". A few keys have special notation: {\tt SPC} is
+the space character, {\tt DEL} is the delete or rubout character, {\tt RET}
+is carriage return, and {\tt ESC} is the escape character. {\tt NUL} is
+the null character (ASCII 0), which is usually equivalent to either
+\verb"C-SPC" or \verb"C-@".
+
+Uppercase and lowercase characters are generally equivalent in command
+keystrokes.
+
+When you run MG from a shell, command line arguments are interpreted as the
+names of files you want to {\em visit\/}, or edit. Each file is
+read into a {\em buffer\/} in memory. No changes are actually made to
+the file until you ask it to be written out to disk.
+
+Within MG, the large top part of the screen serves as a {\em window\/} into
+the buffer being edited. Below this is the {\em mode line\/}, which
+displays the name of the buffer. Finally, at the very bottom of the screen,
+there is a one-line {\em minibuffer\/} which is used for displaying
+messages and answering questions.
+
+MG keeps track of two pointers into each window, the {\em point\/} and the
+{\em mark\/}. The {\em cursor\/} appears at the point in the current
+window, and we often speak of moving the cursor rather than of moving the
+point. The text between the point and the mark is referred to as the {\em
+region\/}.
+
+Some commands deal with {\em words\/} and {\em paragraphs\/}.
+Generally, whitespace and punctuation separate words. Lines that are
+empty or that contain only spaces or tabs separate paragraphs without
+being part of a paragraph. A non-empty line that starts with a space
+or tab also begins a new paragraph.
+
+A number of commands are defined as {\em toggles\/}. If no prefix argument
+is supplied, these commands toggle an action. The action is turned on if a
+negative or zero argument is supplied, and turned on if a positive argument
+is supplied.
+
+\section{Getting Started}
+
+This document is intended primarily as a reference manual. If you
+have never used any Emacs-like text editor before, it is strongly
+suggested that you run the on-line tutorial supplied with the MG
+distribution, instead of reading this manual.
+
+Do not be put off by the large number of commands described in this
+manual! It is possible to get by with only a handful of basic commands.
+Here are the ones that are probably used most frequently:
+
+\begin{define}{\hspace{1in}}
+\item[{\tt C-p}\hfill] Move the cursor to the previous line
+\item[{\tt C-n}\hfill] Move the cursor to the next line
+\item[{\tt C-b}\hfill] Move the cursor backwards
+\item[{\tt C-f}\hfill] Move the cursor forwards
+\item[{\tt C-v}\hfill] Scroll forwards one screenful
+\item[{\tt M-v}\hfill] Scroll backwards one screenful
+\item[{\tt M-<}\hfill] Go to the beginning of the buffer
+\item[{\tt M->}\hfill] Go to the end of the buffer
+\item[{\tt C-a}\hfill] Go to the beginning of the line
+\item[{\tt C-e}\hfill] Go to the end of the line
+\item[{\tt DEL}\hfill] Delete the previous character
+\item[{\tt C-k}\hfill] Kill (delete) to the end of line
+\item[{\tt C-y}\hfill] Reinsert killed text.
+\item[{\tt C-x C-c}\hfill] Exit MG
+\item[{\tt C-x C-s}\hfill] Save the current buffer
+\end{define}
+
+\chapter{Using Commands}
+
+\section{Command Arguments}
+
+Some commands require arguments. For example, if you want to read a
+file into a buffer, you must type in the name of the file. In the
+descriptions of commands in this manual, if arguments are required,
+they are listed following the command name.
+
+MG prompts for command arguments in the minibuffer. Within the minibuffer,
+the following characters can be used for editing:
+
+\begin{define}{\hspace{1in}}
+\item[{\tt DEL, C-h}\hfill] Erase the last character.
+\item[{\tt C-x, C-u}\hfill] Erase the entire input line.
+\item[{\tt C-w}\hfill] Erase to the beginning of the previous word.
+\item[{\tt C-q, $\backslash$}\hfill] Quote the next character typed.
+\item[{\tt RET}\hfill] Signifies that you have completed typing in
+the argument.
+\item[{\tt C-g}\hfill] Abort the command in progress.
+\end{define}
+
+\section{Prefix Arguments}
+
+All commands accept an optional numeric prefix argument. This is
+often interpreted as a repetition count. For example, the function
+{\tt next-line}, if given a prefix argument, will move the cursor
+forward that many lines; without an argument, it will move the cursor
+forward one line. A few commands behave differently if given a prefix
+argument than they do without one, and others ignore the prefix
+argument entirely.
+
+\fname{digit-argument}{}{M-0, M-1, M-2, M-3, M-4, M-5, M-6, M-7, M-8, M-9}
+\fmore{negative-argument}{}{M--}
+\fbody One way to specify a command argument is to use the escape key
+as a meta prefix, and then type one or more digits. A dash may be
+used for a negative argument.
+
+\fname{universal-argument}{}{C-u}
+\fbody Another way to specify a command prefix is to type {\tt C-u}.
+Typing one {\tt C-u} is equivalent to a prefix argument of 4, typing
+two gives a value of 16, and so on. In addition, you can type digits
+following {\tt C-u} to form a numeric prefix argument.
+
+\section{Aborting}
+
+\fname{keyboard-quit}{}{C-g}
+\fbody Typing {\tt C-g} cancels any command. It is particularly useful
+for cancelling a command when MG is prompting for input in the minibuffer.
+
+\section{Extended Commands}
+
+\fname{execute-extended-command}{command}{M-x}
+\fbody Commands that are not bound to keys can be executed through
+{\tt execute extended-command}. If a prefix argument is supplied, it
+is passed to the command being executed.
+
+
+\chapter{Moving the Cursor}
+
+The commands described in this chapter move the cursor (sometimes
+called the point or dot) within the current window. Commands which
+set the mark are included here as well.
+
+\fname{backward-char}{}{C-b}
+\fbody Moves the cursor backward (left) one character. If the cursor
+is at the left margin, it will be moved to the end of the previous line.
+
+\fname{backward-paragraph}{}{M-[}
+\fbody Moves the cursor backwards to the beginning of the current
+paragraph, or to the beginning of the previous paragraph if the cursor
+is already at the beginning of a paragraph.
+
+\fname{backward-word}{}{M-b}
+\fbody Moves the cursor backwards to the beginning of the current word,
+or to the beginning of the previous word if the cursor is already at
+the beginning of a word.
+
+\fname{beginning-of-buffer}{}{M-<}
+\fbody Moves the cursor backwards to the beginning of the buffer.
+
+\fname{beginning-of-line}{}{C-a}
+\fbody Moves the cursor backwards to the beginning of the current
+line. This command has no effect if the cursor is already at the beginning
+of the line.
+
+\fname{end-of-buffer}{}{M->}
+\fbody Moves the cursor forwards to the end of the buffer.
+
+\fname{end-of-line}{}{C-e}
+\fbody Moves the cursor forwards to the end of the current line. This
+command has no effect if the cursor is already at the end of the line.
+
+\fname{exchange-point-and-mark}{}{C-x C-x}
+\fbody Set the mark at the current cursor position, and move the cursor
+to the old location of the mark.
+
+\fname{forward-char}{}{C-f}
+\fbody Moves the cursor forwards one character. If the cursor is at the
+end of a line, it will be moved to the first character on the next line.
+
+\fname{forward-paragraph}{}{M-]}
+\fbody Moves the cursor forwards to the next paragraph delimiter.
+
+\fname{forward-word}{}{M-f}
+\fbody Moves the cursor forwards to the end of the current word, or to
+the end of the next word if the cursor is already at the end of a word.
+
+\fname{goto-line}{line-number}{}
+\fbody Moves the cursor to the beginning of line {\em line-number\/} in
+the buffer.
+
+\fname{next-line}{}{C-n}
+\fbody Moves the cursor down one line. The cursor remains in the same
+column unless it would be past the end of the line, in which case it is
+moved to the end of the line. At the end of the buffer, {\tt C-n} will
+create new lines.
+
+\fname{previous-line}{}{C-p}
+\fbody Moves the cursor up one line. The cursor remains in the same
+column unless it would be past the end of the line, in which case it is
+moved to the end of the line.
+
+\fname{recenter}{}{C-l}
+\fbody Redraws the entire screen, scrolling the current window if necessary
+so that the cursor is near the center. With a positive prefix argument
+{\em n\/}, the window is scrolled so that the cursor is {\em n\/} lines
+from the top. A negative prefix argument puts the cursor that many lines
+from the bottom of the window.
+
+\fname{redraw-display}{}{}
+\fbody Redraws the entire screen, but never scrolls.
+
+\fname{scroll-down}{}{M-v}
+\fbody Scrolls the display down (moving backward through the
+buffer). Without
+an argument, it scrolls slightly less than one windowful. A prefix argument
+scrolls that many lines.
+
+\fname{scroll-one-line-down}{}{}
+\fmore{scroll-one-line-up}{}{}
+\fbody These functions are similar to {\tt scroll-down} and {\tt scroll-up}
+(respectively), but when invoked without an argument, cause the display
+to scroll by one line only. These functions are enabled by defining the
+compile-time option GOSMACS.
+
+\fname{scroll-other-window}{}{M-C-v}
+\fbody Scrolls the ``other'' window forward as for {\tt scroll-up}.
+
+\fname{scroll-up}{}{C-v}
+\fbody Scrolls the display up (moving forward through the buffer). Without an
+an argument, it scrolls slightly less than one windowful. A prefix argument
+scrolls that many lines.
+
+\fname{set-mark-command}{}{NUL}
+\fbody Set the mark at the current cursor position.
+
+\fname{what-cursor-position}{}{C-x =}
+\fbody Prints some information in the minibuffer about where the cursor is.
+
+
+\chapter{Text Insertion Commands}
+
+The usual way to insert text into a buffer is simply to type the
+characters. The default binding for all of the printing characters
+({\tt self-insert-command}) causes them to be inserted literally at
+the cursor position.
+
+\fname{insert}{string}{}
+\fbody Insert {\em string\/} into the current buffer at the cursor position.
+
+\fname{newline}{}{RET}
+\fbody Insert a line break into the current buffer at the cursor position,
+moving the cursor forward to the beginning of the new line.
+
+\fname{newline-and-indent}{}{C-j}
+\fbody Insert a line break into the current buffer at the cursor position,
+then add extra whitespace so that the cursor is aligned in the same
+column as the first non-whitespace character in the previous line.
+
+\fname{open-line}{}{C-o}
+\fbody Inserts a line break into the current buffer at the current position,
+but does not move the cursor forward.
+
+\fname{quoted-insert}{}{C-q}
+\fbody This command acts as a prefix to
+cancel the normal interpretation of the next keystroke. If {\tt C-q}
+is followed by one to three octal digits, it is interpreted as the
+code of the character to insert. Otherwise a single key is read and
+the character typed is inserted into the buffer instead of interpreted
+as a command. This is used for inserting literal control characters
+into a buffer.
+
+\fname{self-insert-command}{}{}
+\fbody This is the default binding for keys representing printable
+characters. The character is inserted into the buffer at the cursor
+position, and the cursor moved forward.
+
+\chapter{Killing, Deleting, and Moving Text}
+
+When text is deleted, it is erased completely. Killing text, on the
+other hand, moves it into a temporary storage area called the kill
+buffer. The saved text in the kill buffer is erased when another
+block of text is killed. Until then, however, you can retrieve text
+from the kill buffer. This can be used to move or copy blocks of
+text, as well as to restore accidentally killed text.
+
+\fname{backward-kill-word}{}{M-DEL}
+\fbody Kill the text backwards from the cursor position to the beginning
+of the current word. Typing {\tt M-DEL} several times in succession
+prepends each killed word to the kill buffer.
+
+\fname{copy-region-as-kill}{}{M-w}
+\fbody Copies the text in the region into the kill buffer, without removing
+it from the current buffer.
+
+\fname{delete-backward-char}{}{DEL}
+\fbody Deletes the character to the left of the cursor.
+
+\fname{delete-blank-lines}{}{C-x C-o}
+\fbody Deletes all blank lines after the current line, and if the current
+line is blank, deletes it and all blank lines preceeding it as well.
+
+\fname{delete-char}{}{C-d}
+\fbody Deletes the character underneath the cursor.
+
+\fname{delete-horizontal-space}{}{M-$\backslash$}
+\fbody Deletes all spaces and tabs on either side of the cursor.
+
+\fname{just-one-space}{}{M-SPC}
+\fbody This is like {\tt delete-horizontal-space}, except it leaves a single
+space at the cursor position.
+
+\fname{kill-line}{}{C-k}
+\fbody If no prefix argument is specified, this function kills text up
+to the next newline; or if the cursor is at the end of a line, the newline
+is killed. A prefix argument specifies how many lines to kill. Typing
+{\tt C-k} several times in succession appends each line to the kill buffer.
+
+\fname{kill-paragraph}{}{}
+\fbody This command kills the entire paragraph containing the cursor.
+If the cursor is positioned between paragraphs, the next paragraph is killed.
+
+\fname{kill-region}{}{C-w}
+\fbody The region (all text between point and mark) is killed.
+
+\fname{kill-word}{}{M-d}
+\fbody Text is killed forward from the cursor position to the next
+end of word. If the cursor is at the end of the word, then the next
+word is killed. Typing {\tt M-d} several times appends the killed
+text to the kill buffer.
+
+\fname{yank}{}{C-y}
+\fbody Text is copied from the kill buffer into the current buffer at
+the cursor position. The cursor is moved to the end of the inserted
+text.
+
+\chapter{Searching and Replacing}
+
+\section{Searching}
+
+The ordinary search command in MG differs from that in many other editors
+in that it is incremental: it begins searching as soon as you begin
+typing the search string, instead of waiting for you to type the entire
+string.
+
+All of the search commands described in this section are case-insensitive.
+
+
+\fname{isearch-backward}{pattern}{C-r}
+\fmore{isearch-forward}{pattern}{C-s}
+\fbody These commands perform an incremental search backward and
+forward (respectively) for {\em pattern\/}. MG will move the cursor
+to the place in the buffer that matches as much of the pattern as you
+have typed so far, as each character is entered.
+
+
+Within the incremental search, the following characters are interpreted
+specially:
+
+\begin{define}{\hspace{1in}}
+
+\item[{\tt DEL}\hfill] Erase the last character in the search string.
+
+\item[{\tt ESC}\hfill] Stop searching; exit from incremental search
+mode, leaving the cursor where the search brought it.
+
+\item[{\tt C-g}\hfill] If a match has been found, exits from
+incremental search but leaves the cursor in its original position. If
+the search has failed, this will just erase the characters which have
+not been found from the end of the search pattern. In this case, you
+must type \verb"C-g" again to abort the search.
+
+\item[{\tt C-s}\hfill] Search forward for the next occurrence of the
+same pattern.
+
+\item[{\tt C-r}\hfill] Search backward for the previous occurrence of
+the same pattern.
+
+\item[{\tt C-q}\hfill] ``Quotes'' the next character typed, forcing it
+to be interpreted as a literal character in the search pattern.
+\end{define}
+
+In addition, normal commands such as \verb"C-a" that do not have special
+meanings within incremental search cause the search to be terminated, and
+then are executed in the ordinary way.
+
+\fname{search-again}{}{}
+\fmore{search-backward}{pattern}{M-r}
+\fmore{search-forward}{pattern}{M-s}
+\fbody These commands perform ordinary, non-incremental searches.
+{\tt Search-again} uses the same pattern and direction as the previous
+search.
+
+\section{Replacing}
+
+\fname{query-replace}{pattern replacement}{M-\%}
+\fbody The primary replace command in MG is an interactive query replace.
+MG searches forward for occurrences of {\em pattern\/}, and asks you what
+to do about each one. The choices are:
+
+\begin{define}{\hspace{1in}}
+
+\item[{\tt SPC}\hfill] Replace this match with {\em replacement\/},
+and go on to the next.
+
+\item[{\tt DEL}\hfill] Skip to the next match without replacing this one.
+
+\item[{\tt .}\hfill] Replace this match, and then quit.
+
+\item[{\tt !}\hfill] Replace all remaining occurrences without asking again.
+
+\item[{\tt ESC}\hfill] Quit.
+\end{define}
+
+By default, {\tt query-replace} adjusts the case of lower-case letters
+in the replacement string to match that of the
+particular occurrence of the pattern; for example, replacing ``Foo''
+with ``bar'' results in ``Bar''. Upper case letters in the replacement
+string are always left uppercase. In addition, supplying a prefix argument
+will also tell {\tt query-replace} to leave the case of the replacement
+string as-is.
+
+Note that {\tt query-replace} always performs a case-insensitive search.
+
+
+
+\section{Regular Expressions}
+
+Regular expressions provide a means for specifying complex search
+patterns, instead of just a literal string. The commands in this
+section are available only if MG is compiled with the REGEX option
+defined.
+
+Regular expression syntax uses the following rules. Most characters
+in a regular expression are considered to be {\em ordinary\/} characters,
+and will match themselves and nothing else. The exceptions are the
+special characters listed below.
+
+\begin{define}{\hspace{1in}}
+
+\item[{\tt .}\hfill] Matches any single character except a newline.
+
+\item[{\tt *}\hfill] A suffix operator that matches zero or more
+repetitions of the (smallest) preceding regular expression.
+
+\item[{\tt +}\hfill] A suffix operator that matches one or more
+repetitions of the (smallest) preceding regular expression.
+
+\item[{\tt ?}\hfill] A suffix operator that matches either zero or one
+occurence of the (smallest) preceding regular expression.
+
+\item[{\tt [\ldots]}\hfill] Matches any one character listed in the
+character set between the square brackets. See examples below.
+
+\item[{\tt \^{ }}\hfill] Matches at the beginning of a line.
+
+\item[{\tt \$}\hfill] Matches at the end of a line.
+
+\item[{\tt $\backslash$}\hfill] Except for the situations listed
+below, acts as a prefix operator which causes the character following
+to be treated as an ordinary character.
+
+\item[{\tt $\backslash$|}\hfill] An infix binary {\em or\/} operator.
+It applies to the two largest surrounding expressions.
+
+\item[{\tt $\backslash$(\ldots$\backslash$)}\hfill] A grouping construct,
+usually used to specify a larger expression for postfix operators such
+as \verb"*" or to limit the scope of operands to \verb"\|".
+
+\item[{\tt $\backslash${\em digit\/}}\hfill] Matches the same text
+matched by the {\em digit\/}th \verb"\(...\)" construct. These are
+numbered from 1 to 9 in the order that the open-parentheses appear.
+
+\item[{\tt $\backslash$`}\hfill] Matches at the beginning of the buffer.
+
+\item[{\tt $\backslash$'}\hfill] Matches at the end of the buffer.
+
+\item[{\tt $\backslash$b}\hfill] Matches at the beginning or end of a word.
+
+\item[{\tt $\backslash$B}\hfill] Matches anyplace {\em except\/} at
+the beginning or end of a word.
+
+\item[{\tt $\backslash$<}\hfill] Matches at the beginning of a word.
+
+\item[{\tt $\backslash$>}\hfill] Matches at the end of a word.
+
+\item[{\tt $\backslash$w}\hfill] Matches any word-constituent character.
+
+\item[{\tt $\backslash$W}\hfill] Matches any character which is {\em
+not\/} a word-constituent.
+
+\end{define}
+
+Some examples may help clarify the rules.
+
+\begin{define}{\hspace{1in}}
+
+\item[{\tt foo}\hfill] Matches the literal string {\tt foo}.
+
+\item[{\tt ;.*}\hfill] Matches all strings which begin with a
+semicolon and continue to the end of a line.
+
+\item[{\tt c[ad]+r}\hfill] Matches strings of the form {\tt car}, {\tt
+cdr}, {\tt caar}, {\tt cadr}, and so on.
+
+\item[{\tt [a-z]}\hfill] Matches any lowercase letter.
+
+\item[{\tt [\^{ }a-z]}\hfill] Matches any character {\em except\/}
+lowercase letters.
+
+\item[{\tt [0-9+---]}\hfill] Matches a digit or sign.
+
+\item[{\tt $\backslash$(foo$\backslash$|bar$\backslash$)}\hfill]
+Matches either the string {\tt foo} or the string {\tt bar}.
+
+\end{define}
+
+\fname{count-matches}{pattern}{}
+\fmore{count-non-matches}{pattern}{}
+\fbody These commands count the number of lines which do or do not
+(respectively) match the specified pattern.
+
+\fname{delete-matching-lines}{pattern}{}
+\fmore{delete-non-matching-lines}{pattern}{}
+\fbody These commands delete all lines which do or do not (respectively)
+match the specified pattern.
+
+
+\fname{query-replace-regexp}{pattern replacement}{}
+\fbody This is the regular expression version of {\tt query-replace}.
+
+The {\em replacement\/} string may be a constant, or it can refer to
+all or part of the string matched by the {\em pattern\/}. \verb"\&" in
+the replacement string expands into the entire text being replaced,
+while \verb"\"{\em n\/} (where {\em n\/} is a number) replaces the
+{\em n\/}th parenthesized expression in {\em pattern\/}.
+
+\fname{re-search-again}{}{}
+\fmore{re-search-backward}{pattern}{}
+\fmore{re-search-forward}{pattern}{}
+\fbody These are the regular expression equivalents of the ordinary
+non-incremental search commands.
+
+\fname{set-case-fold-search}{}{}
+\fbody This command toggles an internal variable that controls whether
+the regular expression search and replace commands pay attention to
+case. By default, regular expression searches are case-insensitive.
+Ordinary searches are always case-insensitive and are not affected by
+the setting of this variable.
+
+
+\chapter{Windows}
+
+MG initially has only one text window displayed. However, you can have
+as many windows as will fit on the screen. Each window has its own mode
+line and must display at least two lines of text. (Note that a MG's
+``windows'' are distinct from the ``windows'' handled by screen managers
+such as the X Window System.)
+
+Multiple windows may be used to display different buffers. You can also
+have the same buffer displayed in more than one window, which is useful
+if you want to see one part of a file at the same time as you are editing
+another part.
+
+Although many windows can be displayed at once, only one window is active
+at any given time. This is the window where the cursor appears.
+
+Some commands refer to the ``other'' window. This is the window directly
+below the current window, or the top window if you are in the bottom window.
+
+\fname{delete-other-windows}{}{C-x 1}
+\fbody Makes the current window the only window.
+
+\fname{delete-window}{}{C-x 0}
+\fbody Deletes the current window, making the ``other'' window the
+current window. This command doesn't do anything useful if there is only
+one window being displayed.
+
+\fname{enlarge-window}{}{C-\^{ }}
+\fbody Makes the current window larger. Without a prefix argument, the
+window grows one line; otherwise, the prefix argument specifies how many
+lines to grow.
+
+\fname{other-window}{}{C-x o}
+\fbody Makes the ``other'' window the current window.
+
+\fname{previous-window}{}{}
+\fbody This is like {\tt other-window}, except that it cycles through
+the windows in reverse order. This command is available only if MG was
+compiled with the GOSMACS option defined.
+
+\fname{shrink-window}{}{}
+\fbody Makes the current window smaller. Without a prefix argument, the
+window loses one line; otherwise, the prefix argument specifies how many
+lines go away.
+
+\fname{split-window-vertically}{}{C-x 2}
+\fbody Split the current window into two windows, both using the same
+buffer.
+
+
+\chapter{Files and Buffers}
+
+Most buffers are used to contain a file being edited. It is
+also possible to have buffers that are not associated with any file;
+MG uses these for purposes such as displaying help text, for example.
+However, since most commands for dealing with files also deal with
+buffers, we have grouped all of these commands together into one chapter.
+
+\section{Buffer Manipulation}
+
+\fname{insert-buffer}{buffer-name}{}
+\fbody Inserts the contents of the named buffer into the current buffer
+at the cursor location. The cursor moves to the end of the inserted
+text.
+
+\fname{kill-buffer}{buffer-name}{C-x k}
+\fbody The named buffer and its contents are deleted. If the buffer has
+been marked as modified, MG will ask you if you really want to delete it.
+Note that, contrary to its name, this command {\em does not\/} save the
+buffer contents in the kill buffer.
+
+If a buffer is being displayed in a window when it is deleted, MG will
+find some other buffer to display in the same window.
+
+\fname{list-buffers}{}{C-x C-b}
+\fbody This command writes information about the buffers currently in
+use to a buffer named {\tt *Buffer List*}. This buffer is then displayed
+in the ``other'' window; if there is only one window, this command will
+split the screen into two windows.
+
+\fname{not-modified}{}{M-\~{ }}
+\fbody This command makes MG think that the current buffer has not been
+modified, even if it really has been changed. This affects the behavior
+of the {\tt kill-buffer} and the buffer-saving commands described below.
+
+MG indicates modified buffers with two stars at the left end of the mode
+line.
+
+\fname{switch-to-buffer}{buffer-name}{C-x b}
+\fbody The current window is mapped onto the named buffer. If there
+isn't already a buffer with that name around, MG will create one.
+
+\fname{switch-to-buffer-other-window}{buffer-name}{C-x 4 b}
+\fbody This command works like {\tt switch-to-buffer}, except that the
+``other'' window is used. If there is only one window, this command
+splits the screen into two windows and maps the named buffer onto one
+of them.
+
+\section{Reading and Writing Files}
+
+\fname{find-file}{file-name}{C-x f}
+\fmore{find-file-other-window}{file-name}{C-x 4 C-f}
+\fbody These commands are analagous to {\tt switch-to-buffer} and {\tt
+switch-to-buffer-other-window}, respectively. The difference is that
+these commands look for a buffer associated with the named file. If no
+matching buffer is found, MG will create a new buffer with a name
+derived from the filename, and attempt to read the file into the buffer.
+If the named file cannot be opened, the buffer remains empty.
+
+\fname{insert-file}{file-name}{C-x i}
+\fbody This command reads in the contents of the named file into the
+current buffer at the cursor position. The cursor remains in the same
+place.
+
+\fname{save-buffer}{}{C-x C-s}
+\fbody If the current buffer has been modified, it is saved. Buffers
+that are not associated with files cannot be written out with this
+command.
+
+\fname{save-buffers-kill-emacs}{}{C-x C-c}
+\fbody This command is used to leave MG and return control to the shell
+or other program that was used to start MG. If there are modified buffers,
+MG will ask you if you want to save them before exiting.
+
+\fname{save-some-buffers}{}{C-x s}
+\fbody MG will ask you if you want to save modified buffers that are
+associated with files.
+
+\fname{write-file}{file-name}{C-x C-w}
+\fbody The current buffer is written out using the file name supplied.
+This is useful for saving buffers that are not associated with files, or
+for writing out a file with a different name than what was used to read
+it in.
+
+\section{Backup Files}
+
+MG provides a way to save a copy of the original version of files which
+have been modified and then written out again. The backup copy reflects
+the state of the file as it existed the first time it was read into MG.
+The name used for the backup file varies, depending on the operating
+system.
+
+This feature is disabled if MG is compiled with NO\_BACKUP defined.
+
+\fname{make-backup-files}{}{}
+\fbody This command is a toggle which
+controls the state of an internal variable that determines whether MG
+creates backup files.
+
+\section{Changing the Directory}
+
+The commands in this section are disabled by defining NO\_DIR.
+
+\fname{cd}{directory-name}{}
+\fbody This command changes MG's notion of the ``current'' directory
+or pathname. This is used to supply defaults for functions that read
+or write files.
+
+The syntax for {\em directory-name\/} is obviously specific to the
+particular operating system MG is running on.
+
+\fname{pwd}{}{}
+\fbody Display what MG thinks is the current directory.
+
+\chapter{Modes}
+
+Modes are used to locally alter the bindings of keys on a
+buffer-by-buffer basis. MG is normally in fundamental mode, and these
+are the bindings that are listed with the command descriptions in
+this manual. Modes define additional keymaps that are searched for
+bindings before the fundamental mode bindings are examined; see the
+section on key binding below for more details on how this works.
+
+\fname{set-default-mode}{mode-name}{}
+\fbody Normally, when MG visits a file, it puts the associated buffer
+into fundamental mode. Using the {\tt set-default-mode} command, you
+can specify that MG should default to use some other mode on all subsequent
+buffers that are created. This command is a toggle. With no prefix
+argument, if the named mode is not already on the list of
+default modes, then it will be added to the list; otherwise, it is removed
+from the list.
+
+\section{No Tab Mode}
+
+In notab mode, tabs are expanded into spaces instead of inserted
+literally into the buffer. Literal tab characters are displayed as
+\verb"^I" (much like other control characters). These commands are
+available if MG is compiled with the symbol NOTAB defined. (This mode
+is mainly for use on systems such as PRIMOS that do not treat tab as a
+series of spaces.)
+
+\fname{no-tab-mode}{}{}
+\fbody This command is a toggle to control whether notab mode is in effect.
+
+\fname{space-to-tabstop}{}{}
+\fbody Insert enough spaces to move the cursor to the next tab stop. In
+notab mode, this function is bound to {\tt C-i}.
+
+
+\section{Overwrite Mode}
+
+Normally, when characters are inserted into the buffer, they are spliced
+into the existing text. In overwrite mode, inserting a character causes
+the character already at the cursor position to be replaced. This is
+useful for editing pictures, tables, and the like.
+
+\fname{overwrite-mode}{}{}
+\fbody This command is a toggle which controls whether overwrite mode is
+in effect.
+
+\section{Auto Fill}
+
+Fill mode causes newlines to be added automatically at word
+breaks when text is added at the end of a line, extending past the
+right margin. Auto fill is useful for editing text and documentation
+files.
+
+\fname{auto-fill-mode}{}{}
+\fbody This command is a toggle which controls whether fill mode is
+in effect.
+
+\fname{insert-with-wrap}{}{}
+\fbody This command works like {\tt self-insert}, except that it checks
+to see if the cursor has passed the right margin. If so, it fills
+the line by inserting a line break between words. This command is bound to
+{\tt SPC} in fill mode.
+
+\fname{fill-paragraph}{}{M-q}
+\fbody Fill the paragraph containing the cursor.
+
+\fname{set-fill-column}{}{C-x f}
+\fbody Without a prefix argument, this command sets the right margin
+at the current cursor column. If a prefix argument is supplied, it is used
+instead as the line width.
+
+\section{Auto Indent}
+
+Indent mode binds {\tt RET} to {\tt newline-and-indent}, so
+that each new line is indented to the same level as the preceeding
+line. This mode is useful for editing code.
+
+\fname{auto-indent-mode}{}{}
+\fbody This command is a toggle which controls whether auto-indent mode
+is in effect.
+
+\section{Blink}
+
+Blink mode makes it easier to match parentheses, brackets, and other
+paired delimiters. When the closing delimiter is typed, the cursor
+moves momentarily to the matching opening delimiter (if it is on the
+screen), or displays the line containing the matching delimiter on the
+echo line. This is useful for editing Lisp or C code, or for
+preparing input files for text processors such as LaTeX that use
+paired delimiters.
+
+\fname{blink-matching-paren}{}{}
+\fbody This command is a toggle which controls whether blink mode is
+in effect.
+
+\fname{blink-matching-paren-hack}{}{}
+\fbody This function behaves like {\tt self-insert}, except that it
+finds the matching delimiter as described above. In blink mode, this
+function is bound to \verb")", which flashes the matching \verb"(". This
+function also knows about the pairs \verb"{}", \verb"[]", and \verb"<>".
+All other characters match with themselves.
+
+\section{Dired Mode}
+
+``Dired'' is an abbreviation for ``directory editor'', and it provides a way
+to browse through the contents of a directory from with MG. Dired puts
+a directory listing into a buffer; you can use normal editing commands to
+move around the buffer, and a special group of commands to manipulate
+the files. For example, there are commands to delete and rename files,
+and to read a file into an MG buffer.
+
+Since dired mode rebinds many keys, a table may be helpful:
+
+\begin{verbatim}
+ C-d dired-flag-file-deleted
+ SPC next-line
+ c dired-copy-file
+ d dired-flag-file-deleted
+ e dired-find-file
+ f dired-find-file
+ n next-line
+ o dired-find-file-other-window
+ p previous-line
+ r dired-renamefile
+ u dired-unflag
+ x dired-do-deletions
+ DEL dired-backup-unflag
+\end{verbatim}
+
+The commands in this section are disabled by defining NO\_DIRED.
+
+\fname{dired}{directory-name}{C-x d}
+\fbody Creates a dired buffer for the given directory name, and displays
+it in the current window. The files
+in the directory are listed, usually along with information about the
+file such as its size and timestamp. The exact format of the information
+is system-specific.
+
+\fname{dired-backup-unflag}{}{}
+\fbody This function removes the deletion flag from the file listed on
+the previous line of the dired buffer.
+
+\fname{dired-copy-file}{new-name}{}
+\fbody Copy the file listed on the current line of the dired buffer.
+
+\fname{dired-do-deletions}{}{}
+\fbody Deletes the files that have been flagged for deletion.
+
+\fname{dired-find-file}{}{}
+\fmore{dired-find-file-other-window}{}{}
+\fbody These function works like {\tt find-file} and
+{\tt find-file-other-window}, except that the filename is taken
+from the current line in the dired buffer.
+
+\fname{dired-flag-file-deleted}{}{}
+\fbody 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 not actually deleted until the function {\tt dired-do-deletions}
+is executed.
+
+\fname{dired-other-window}{directory-name}{}
+\fbody This function works just like {\tt dired}, except that it puts the
+dired buffer in the ``other'' window.
+
+\fname{dired-rename-file}{new-name}{}
+\fbody Renames the file listed on the current line of the dired buffer.
+Note that the dired buffer is not updated to reflect the change.
+
+\fname{dired-unflag}{}{}
+\fbody Remove the deletion flag for the file on the current line.
+
+\chapter{Miscellaneous}
+
+\section{Help}
+
+Most of the commands in this section write useful information to the
+{\tt *help*} buffer, which is then displayed in the ``other'' window.
+
+These commands can be disabled at compile-time by defining NO\_HELP.
+
+\fname{apropos}{topic}{C-h a}
+\fbody This command lists all functions whose names contain a string
+matching {\em topic\/} in the {\tt *help*} buffer.
+
+\fname{describe-bindings}{}{C-h b}
+\fbody Information about the key bindings in effect in the current buffer
+is listed in the {\tt *help*} buffer.
+
+\fname{describe-key-briefly}{key}{C-h c}
+\fbody Information about the binding of {\em key\/} is printed in the
+minibuffer.
+
+\fname{help-help}{option}{C-h C-h}
+\fbody This command lists all of the help options available and
+prompts for which one to run. Currently, these include only {\tt a}
+to run {\tt apropos}, {\tt b} to run {\tt describe-bindings}, and {\tt c}
+to run {\tt describe-key-briefly}.
+
+
+\section{Keyboard Macros}
+
+A keyboard macro is a saved set of commands from the keyboard that can be
+reexecuted later on. There can only be one keyboard macro defined at
+any one time.
+
+The commands in this section are available unless they have been disabled
+by defining NO\_MACRO.
+
+\fname{call-last-kbd-macro}{}{C-x e}
+\fbody Execute the saved keyboard macro. A prefix argument can be used
+to specify a repetition count.
+
+\fname{end-kbd-macro}{}{C-x )}
+\fmore{start-kbd-macro}{}{C-x (}
+\fbody These functions are used to define a keyboard macro. All keys
+entered after {\tt start-kbd-macro} is executed, up to a {\tt end-kbd-macro},
+are remembered as they are executed. You can then reexecute the same
+sequence of operations using {\tt call-last-kbd-macro}.
+
+
+\section{Changing Case}
+
+MG provides a number of functions for changing the case of text.
+
+\fname{capitalize-word}{}{M-c}
+\fmore{downcase-region}{}{C-x C-l}
+\fmore{downcase-word}{}{M-l}
+\fmore{upcase-region}{}{C-x C-u}
+\fmore{upcase-word}{}{M-u}
+\fbody All of these commands do the obvious.
+
+
+\section{Odds and Ends}
+
+This section describes miscellaneous commands that don't fit into any
+particular category.
+
+\fname{emacs-version}{}{}
+\fbody Prints information about the version of MG you are running in
+the minibuffer.
+
+\fname{meta-key-mode}{}{}
+\fbody If the particular version of MG you are running supports a meta key,
+this function can be used to determine whether MG actually pays attention
+to it or not. If no prefix argument is supplied, the internal variable
+that controls the use of the meta key is toggled; a positive value enables
+the meta key, while a negative value disables it.
+
+\fname{prefix-region}{}{}
+\fmore{set-prefix-string}{string}{}
+\fbody {\tt Prefix-region} is used to prefix each line of the region
+with a string. This is useful for indenting quoted text, making block
+comments, and the like. The function {\tt set-prefix-string} can be
+used to set the string used as the prefix.
+
+\fname{suspend-emacs}{}{C-z}
+\fbody This command temporarily suspends
+MG so that you can run other programs, and later resume editing. The
+exact behavior depends on which operating system you are running MG
+under. Typically, MG will either spawn a new shell as a subprocess, or
+return you to the parent process.
+
+\fname{transpose-chars}{}{C-t}
+\fbody This command transposes the previous two characters.
+
+
+\chapter{Customization}
+
+MG provides a limited support for customization. However, unlike ``real''
+Emacs, there is no extension language for interpretively defining new
+functions.
+
+\section{Key Bindings}
+
+MG allows keys to be rebound locally or globally. To understand the
+difference between the two, some discussion on how modes are implemented
+is necessary.
+
+An internal data structure called a keymap is used to look up the
+function that is bound to a particular key. The keymap for
+fundamental mode contains all of the default bindings which are listed
+with the command descriptions in this manual. Modes define additional
+keymaps that are searched for a binding before the fundamental mode
+keymap is examined. Keymaps have the same name as the mode they are
+associated with.
+
+MG does not provide commands for defining new modes, but you can alter
+the keymaps for existing modes.
+
+\fname{define-key}{keymap-name key command}{}
+\fbody This command can be used to modify the keymap for the named mode.
+
+\fname{global-set-key}{key command}{}
+\fmore{global-unset-key}{key}{}
+\fbody These commands modify the keymap for fundamental mode. Bindings
+established by {\tt global-set-key} will be inherited by all other modes,
+as long as they do not establish local rebindings of the same key.
+
+\fname{local-set-key}{key command}{}
+\fmore{local-unset-key}{key}{}
+\fbody These commands modify the keymap currently in effect.
+
+
+\section{Startup Files}
+
+Although MG does not include a general-purpose extension language, it
+does provide a way to read and evaluate commands using a somewhat
+different syntax than that used for executing extended commands. This
+is typically used in a startup file to modify key bindings.
+
+A startup file consists of one or more expressions. Each expression must
+appear on a separate line in the file; there may not be more than one
+expression per line, nor may expressions span across line breaks.
+Whitespace (spaces and tabs) separate the tokens in an expression. For
+historical reasons, parentheses are also considered to be whitespace in
+this context. A semicolon acts as a comment character, causing the rest
+of the line to be discarded.
+
+An expression consists of a function name, an optional prefix argument
+(given as an integer constant), and arguments to be passed to the
+function. If an argument includes literal whitespace or nonprintable
+characters (for example, as in a keystroke argument to one of the key
+binding functions described in the previous section), it must be
+supplied as a string constant enclosed in double quotes.
+
+Within string constants, the following backslash escapes are available
+to specify nonprintable characters:
+
+\begin{define}{\hspace{1in}}
+
+\item[{\tt $\backslash$t, $\backslash$T}\hfill] Tab
+\item[{\tt $\backslash$n, $\backslash$N}\hfill] Newline
+\item[{\tt $\backslash$r, $\backslash$R}\hfill] Carriage return
+\item[{\tt $\backslash$e, $\backslash$E}\hfill] Escape (Meta prefix)
+\item[{\tt $\backslash$\^{ }}\hfill] Control prefix
+
+\item[{\tt $\backslash${\em n\/}}\hfill] Specifies a character by its
+ASCII code, where {\em n\/} may consist of from one to three octal
+digits
+
+\item[{\tt $\backslash$f{\em n\/}, $\backslash$F{\em n\/}}\hfill]
+Specifies the keycode for the {\em n\/}th function key. {\em N\/} may
+consist of one or two decimal digits.
+
+\end{define}
+
+The following commands which deal with evaluation of expressions are
+disabled by defining the compile-time option NO\_STARTUP.
+
+The Rutgers Sun version will attempt to read two different startup
+files, a general startup file and a terminal-specific startup file.
+The terminal-specific startup file is intended primarily to define the
+keypad. The general startup file is .mg in your home directory. If
+there is no such file, /usr/local/lib/mg/mg will be used. The
+terminal-specific startup file is .mg-TYPE, where TYPE represents the
+name of the terminal type. E.g if your terminal type is set to vt100,
+MG will read a file .mg-vt100. If there is no such file, it will try
+/usr/local/lib/mg/mg-vt100. Files should exist in /usr/local/lib/mg
+for the terminal types commonly in use at Rutgers.
+
+For other versions, see the implementation notes for your particular
+version of MG for information on how it handles startup files.
+
+
+\fname{eval-current-buffer}{}{}
+\fbody Evaluate the expressions in the current buffer.
+
+\fname{eval-expression}{expression}{}
+\fbody Evaluate the expression supplied.
+
+\fname{load}{file-name}{}
+\fbody Read in the specified file and evaluate its contents.
+
+\twocolumn[\Huge{\vspace{2em}{\bf Fundamental Mode Key Bindings}\vspace{1.5em}}]
+\addcontentsline{toc}{chapter}{Fundamental Mode Key Bindings}
+\begin{verbatim}
+NUL set-mark-command
+C-a beginning-of-line
+C-b backward-char
+C-d delete-char
+C-e end-of-line
+C-f forward-char
+C-g keyboard-quit
+C-h help
+TAB self-insert-command
+C-j newline-and-indent
+C-k kill-line
+C-l recenter
+RET newline
+C-n next-line
+C-o open-line
+C-p previous-line
+C-q quoted-insert
+C-r isearch-backward
+C-s isearch-forward
+C-t transpose-chars
+C-u universal-argument
+C-v scroll-up
+C-w kill-region
+C-x c-x prefix
+C-y yank
+C-z suspend-emacs
+ESC meta prefix
+SPC .. ~ self-insert-command
+DEL delete-backward-char
+
+C-h C-g keyboard-quit
+C-h C-h help-help
+C-h a apropos
+C-h b describe-bindings
+C-h c describe-key-briefly
+
+C-x C-b list-buffers
+C-x C-c save-buffers-kill-emacs
+C-x C-f find-file
+C-x C-g keyboard-quit
+C-x C-l downcase-region
+C-x C-o delete-blank-lines
+C-x C-s save-buffer
+C-x C-u upcase-region
+C-x C-w write-file
+C-x C-x exchange-point-and-mark
+C-x ( start-kbd-macro
+C-x ) end-kbd-macro
+C-x 0 delete-window
+C-x 1 delete-other-windows
+C-x 2 split-window-vertically
+C-x 4 c-x 4 prefix
+C-x = what-cursor-position
+C-x ^ enlarge-window
+C-x b switch-to-buffer
+C-x d dired
+C-x e call-last-kbd-macro
+C-x f set-fill-column
+C-x i insert-file
+C-x k kill-buffer
+C-x o other-window
+C-x s save-some-buffers
+C-x 4 C-f find-file-other-window
+C-x 4 C-g keyboard-quit
+C-x 4 b switch-to-buffer-other-window
+C-x 4 f find-file-other-window
+
+M-C-g keyboard-quit
+M-C-v scroll-other-window
+M-SPC just-one-space
+M-% query-replace
+M-- negative-argument
+M-0 digit-argument
+M-1 digit-argument
+M-2 digit-argument
+M-3 digit-argument
+M-4 digit-argument
+M-5 digit-argument
+M-6 digit-argument
+M-7 digit-argument
+M-8 digit-argument
+M-9 digit-argument
+M-< beginning-of-buffer
+M-> end-of-buffer
+M-[ backward-paragraph
+M-\ delete-horizontal-space
+M-] forward-paragraph
+M-b backward-word
+M-c capitalize-word
+M-d kill-word
+M-f forward-word
+M-l downcase-word
+M-q fill-paragraph
+M-r search-backward
+M-s search-forward
+M-u upcase-word
+M-v scroll-down
+M-w copy-region-as-kill
+M-x execute-extended-command
+M-~ not-modified
+M-DEL backward-kill-word
+
+\end{verbatim}
+
+\begin{theindex}
+\addcontentsline{toc}{chapter}{Index}
+\input{mgidx.tex}
+\end{theindex}
+
+\end{document}
diff --git a/usr.bin/mg/mgidx.tex b/usr.bin/mg/mgidx.tex
new file mode 100644
index 00000000000..aa005a0cb59
--- /dev/null
+++ b/usr.bin/mg/mgidx.tex
@@ -0,0 +1,131 @@
+\indexentry{apropos}{31}
+\indexentry{auto-fill-mode}{27}
+\indexentry{auto-indent-mode}{28}
+\indexentry{backward-char}{9}
+\indexentry{backward-kill-word}{14}
+\indexentry{backward-paragraph}{9}
+\indexentry{backward-word}{9}
+\indexentry{beginning-of-buffer}{9}
+\indexentry{beginning-of-line}{9}
+\indexentry{blink-matching-paren}{28}
+\indexentry{blink-matching-paren-hack}{28}
+\indexentry{call-last-kbd-macro}{32}
+\indexentry{capitalize-word}{32}
+\indexentry{cd}{25}
+\indexentry{copy-region-as-kill}{14}
+\indexentry{count-matches}{19}
+\indexentry{count-non-matches}{19}
+\indexentry{define-key}{34}
+\indexentry{delete-backward-char}{14}
+\indexentry{delete-blank-lines}{14}
+\indexentry{delete-char}{14}
+\indexentry{delete-horizontal-space}{15}
+\indexentry{delete-matching-lines}{19}
+\indexentry{delete-non-matching-lines}{19}
+\indexentry{delete-other-windows}{21}
+\indexentry{delete-window}{21}
+\indexentry{describe-bindings}{31}
+\indexentry{describe-key-briefly}{31}
+\indexentry{digit-argument}{8}
+\indexentry{dired}{29}
+\indexentry{dired-backup-unflag}{29}
+\indexentry{dired-copy-file}{29}
+\indexentry{dired-do-deletions}{29}
+\indexentry{dired-find-file}{29}
+\indexentry{dired-find-file-other-window}{29}
+\indexentry{dired-flag-file-deleted}{29}
+\indexentry{dired-other-window}{30}
+\indexentry{dired-rename-file}{30}
+\indexentry{dired-unflag}{30}
+\indexentry{downcase-region}{32}
+\indexentry{downcase-word}{32}
+\indexentry{emacs-version}{32}
+\indexentry{end-kbd-macro}{32}
+\indexentry{end-of-buffer}{9}
+\indexentry{end-of-line}{10}
+\indexentry{enlarge-window}{21}
+\indexentry{eval-current-buffer}{36}
+\indexentry{eval-expression}{36}
+\indexentry{exchange-point-and-mark}{10}
+\indexentry{execute-extended-command}{8}
+\indexentry{fill-paragraph}{27}
+\indexentry{find-file}{24}
+\indexentry{find-file-other-window}{24}
+\indexentry{forward-char}{10}
+\indexentry{forward-paragraph}{10}
+\indexentry{forward-word}{10}
+\indexentry{global-set-key}{34}
+\indexentry{global-unset-key}{34}
+\indexentry{goto-line}{10}
+\indexentry{help-help}{31}
+\indexentry{insert}{12}
+\indexentry{insert-buffer}{23}
+\indexentry{insert-file}{24}
+\indexentry{insert-with-wrap}{27}
+\indexentry{isearch-backward}{16}
+\indexentry{isearch-forward}{16}
+\indexentry{just-one-space}{15}
+\indexentry{keyboard-quit}{8}
+\indexentry{kill-buffer}{23}
+\indexentry{kill-line}{15}
+\indexentry{kill-paragraph}{15}
+\indexentry{kill-region}{15}
+\indexentry{kill-word}{15}
+\indexentry{list-buffers}{23}
+\indexentry{load}{36}
+\indexentry{local-set-key}{34}
+\indexentry{local-unset-key}{35}
+\indexentry{make-backup-files}{25}
+\indexentry{meta-key-mode}{32}
+\indexentry{negative-argument}{8}
+\indexentry{newline}{12}
+\indexentry{newline-and-indent}{12}
+\indexentry{next-line}{10}
+\indexentry{no-tab-mode}{26}
+\indexentry{not-modified}{24}
+\indexentry{open-line}{12}
+\indexentry{other-window}{21}
+\indexentry{overwrite-mode}{27}
+\indexentry{prefix-region}{33}
+\indexentry{previous-line}{10}
+\indexentry{previous-window}{22}
+\indexentry{pwd}{25}
+\indexentry{query-replace}{17}
+\indexentry{query-replace-regexp}{19}
+\indexentry{quoted-insert}{12}
+\indexentry{re-search-again}{20}
+\indexentry{re-search-backward}{20}
+\indexentry{re-search-forward}{20}
+\indexentry{recenter}{10}
+\indexentry{redraw-display}{11}
+\indexentry{save-buffer}{24}
+\indexentry{save-buffers-kill-emacs}{24}
+\indexentry{save-some-buffers}{25}
+\indexentry{scroll-down}{11}
+\indexentry{scroll-one-line-down}{11}
+\indexentry{scroll-one-line-up}{11}
+\indexentry{scroll-other-window}{11}
+\indexentry{scroll-up}{11}
+\indexentry{search-again}{17}
+\indexentry{search-backward}{17}
+\indexentry{search-forward}{17}
+\indexentry{self-insert-command}{13}
+\indexentry{set-case-fold-search}{20}
+\indexentry{set-default-mode}{26}
+\indexentry{set-fill-column}{27}
+\indexentry{set-mark-command}{11}
+\indexentry{set-prefix-string}{33}
+\indexentry{shrink-window}{22}
+\indexentry{space-to-tabstop}{26}
+\indexentry{split-window-vertically}{22}
+\indexentry{start-kbd-macro}{32}
+\indexentry{suspend-emacs}{33}
+\indexentry{switch-to-buffer}{24}
+\indexentry{switch-to-buffer-other-window}{24}
+\indexentry{transpose-chars}{33}
+\indexentry{universal-argument}{8}
+\indexentry{upcase-region}{32}
+\indexentry{upcase-word}{32}
+\indexentry{what-cursor-position}{11}
+\indexentry{write-file}{25}
+\indexentry{yank}{15}
diff --git a/usr.bin/mg/mgprog.doc b/usr.bin/mg/mgprog.doc
new file mode 100644
index 00000000000..1fc9f8e2703
--- /dev/null
+++ b/usr.bin/mg/mgprog.doc
@@ -0,0 +1,295 @@
+This documentation covers mg 2a.
+
+I do want feedback from other mg developers on what they think of my
+changes, documentation, and what needs to be done to make mg better.
+This document is not complete, it mainly covers the areas I have
+recently changed.
+
+Possible future changes:
+
+Rearange file contents along more rational lines. Further split the
+monolithic def.h file.
+
+Changing the echo line stuff to use a minibuffer keymap.
+
+Making the kill buffer a linked list of lines.
+
+Variables.
+
+Allow for backspace, ^s, etc. to be changed in some reasonable manner.
+(Probably using variables or a simulation thereof.) I do not think an
+input keymap is the correct solution, even if it is frequently used in
+Gnu emacs. (Besides the extra overhead, keynames come out wrong.)
+
+Make long lines wrap like they do in GNU emacs.
+
+Fix known (and unknown :-) bugs.
+
+Have the keymaps and associated tables generated by a program.
+
+
+Known bugs/limitations:
+
+Binding a key in a named keymap may or may not change the binding of
+other keys pointing to the same keymap. (i.e. if ^H and ^_ are bound
+to help, rebinding ^Hb may not (or may) change ^_b. This can be cured
+by rebinding ^_ to help.)
+
+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.
+
+
+
+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.
+
+
+
+Adding command functions to mg:
+
+Command functions take two integer aguments and return an integer.
+The first argument, f, is a set of flags. (f&FFARG) is non-zero if a
+numeric arguement was passed to the function. (There are bits
+indicating how the agument was specified, but they are not fully
+impleminted.) (f&FFRAND) is non-zero if the function is being called
+by another function and that possibly slightly different action should
+be taken. (No error checking, supress output, etc.) The second
+argument, n, is the numeric agument passed or one if there was no
+numeric arugment. The fuction should return TRUE if it executes
+correctly, FALSE if it could not, and ABORT if the user typed the
+keyboard quit character at a prompt.
+
+The function must be added to the functnames table in keymap.c. This
+table must be kept in assending ascii sequence.
+
+
+
+Key maps:
+
+Key maps are structures containing information on what action should
+be taken corresponding to an individual keypress. That action could
+be an indication that this is a prefix key and the next kepress should
+be looked up in another keymap.
+
+ Example keymap:
+
+ static struct KEYMAPE(6+IMAPEXT) cXmap = {
+ 6,
+ 6+IMAPEXT,
+ rescan,
+ {
+ {CCHR('B'),CCHR('G'), cXcB, (KEYMAP *)NULL},
+ {CCHR('L'),CCHR('X'), cXcL, (KEYMAP *)NULL},
+ {'(', ')', cXlp, (KEYMAP *)NULL},
+ {'0', '4', cX0, (KEYMAP *)&cX4map},
+ {'=', '=', cXeq, (KEYMAP *)NULL},
+ {'^', 's', cXcar, (KEYMAP *)NULL},
+ }
+ };
+
+(Note: this example is a simplified example of a real keymap in keymap.c.)
+
+Since C does not directly support structures containing undementioned
+arrays, the macro KEYMAPE is used to create a structure with an array
+of the proper size. 6 is the current size of the array, and IMAPEXT
+is the number of extra elements left for future groth (by rebinding
+keys) before the map must be reallocated. rescan is the function to
+be executed if a specific entry for the key is not found. (rescan is
+a special function that searches for something else to do -- first by
+trying lowercasing the last character in the keymap, then by trying
+the other modes in effect.)
+
+The array elements must be in order by the keys they define. Each
+covers a range of characters. Numeric values should not be used for
+characters, they make porting mg to some other systems harder. The
+CCHR macro may be used to specify control characters, including DEL
+(CCHR('?')). cXcB, cXcL, etc. are arrays of pointers to functions
+returning int. One of these fuction pointers per element may be to
+the pseuto-function prefix. cX4map is the keymap coresponding to the
+prefix function bound to '4' in this keymap. Having several keys in
+an element bound to the default function is better than increasing the
+number of elements.
+
+
+
+Modes:
+
+Modes are named key maps that are scanned for key bindings before the
+global keymap is. There are functions in modes.c to toggle modes on
+or off for individual buffers. Note that the "major"/"minor" mode
+distiction is different than in Gnu Emacs. Dired is currently the
+only major mode available, buffers are put into dired mode on creation
+by the dired code. (The distiction in mg is the major mode replaces
+the default keymap instead of being an overlay.) Some modes (overwrite
+and no-tab) also trigger per-buffer flags that should be convered to
+per-buffer variables when we add variables. Keymaps for the modes are
+kept in keymap.c with the other keymaps.
+
+
+
+System dependent files:
+
+Fileio.c:
+ Contains file i/o routines:
+ ffputbuf(BUFFER *):
+Write all lines of buffer to the file. The last line of the buffer does
+NOT have the normally implied newline. (One may be added if
+nessisary, but don't write out the last line if it is zero characters
+long.)
+
+ ffgetline(char *buf, int nbuf, int *nbytes):
+Read a line from the file up to the length specified by the second
+argument in to the buffer pointed to by the first. If a newline is
+not found after reading nbuf characters, return FIOLONG and on the
+next call to ffgetline return the remaining portion (or nbuf more
+characters and return FIOLONG). If a newline is found, set *nbytes to
+the number of characters read and return FIOSUC. If the end of file
+is descovered, set *nbytes and return FIOEOF. (If the file ends in a
+newline, the call returning FIOEOF will set *nbytes to 0.) If any
+other error occurs, return FIOERR.
+
+ char *adjustname(char *fname)
+Standardize filename. On mono-case systems, lowercase the file name.
+If NO_DIR is not defined, make fully qualified. (not relitive to the
+current directory, which may change.)
+
+ fncmp(char *fna, char *fnb)
+Return 0 if both arguments refer to the same file or buffer. #define
+it to be strcmp on mono-case or case sensitive systems, use a non-case
+sensitive compareison otherwise. (Borrow it from the osk fileio.c)
+Both arguments have been through adjustname already.
+
+ routines needed for dired:
+
+ rename(char *fromname, char *toname)
+rename file. BSD systems have this as a system call. return -1 on
+error.
+
+ copy(char *fromname, char *toname)
+copy file. return -1 on error.
+
+ unlinkdir(char *name)
+Delete directory. return -1 on error. (possibly including non-empty
+directory.)
+
+ BUFFER *dired_(char *name)
+Create dired buffer for named directory. See example from osk or bsd.
+
+ d_makename(LINE *l, char *fname)
+Concatinate directory name associated with the current dired BUFFER
+with file name in line. Return ABORT if no file name on line, FALSE
+if it is an ordinary file, or TRUE if there is a directory. Name made
+should be the same as if it were run through adjustname.
+
+
+Converting system & terminal dependent files from mg1b:
+
+All command functions will have to be rewritten to use the new two
+argument calling sequence. (Some compilers will let you get away
+without doing this for testing purposes. Problems, if any, will show
+up at run-time.)
+
+Key binding is completly different. See extend.c if your code needs
+to do rebinding.
+
+I have attempted make mg less dependant on ascii character values.
+Keymap.c depends on ascii sorting order. Cinfo.c depends on character
+values and contains a function to convert from a character value
+to a key name. Several modules assume 3 octal digits are enough for
+any character value.
+
+Names of most compile time options have changed. Whatever is most
+GNU-emacs like is now the default.
+
+sysdef.h:
+ The type KEY should not be defined. The type KCHAR should be defined.
+ All posible key inputs must be positive in type KCHAR. short is
+ recomended.
+
+spawn.c:
+ Update to the new function calling conventions.
+
+Makefile:
+ Needs complete rewrite. You can probably figure out the dependencies
+ from the bsd or osk one. Compile time options have changed.
+
+Fileio.c:
+ Remove function ffputline. Add function ffputbuf (described above).
+ Rewrite ffgetline to conform to the new way of doing things. (see
+ above.) These routines can probably be borrowed intact from the
+ OSK system dependant fileio.c for unix systems.
+ Replace adjustcase with adjustname. Add fncmp either here or
+ as a #define in sysdef.h. Add functions needed by dired.
+
+
+
+
+Compile time options:
+
+extentions not directly in gnu emacs
+
+BSMAP input mapping exchanging ^H and DEL.
+ 1 for defaulting to this, 0 for normal default.
+NOTAB for systems that don't like tabs
+CVMVAS arguments to ^V in screens not lines
+PREFIXREGION prefix region
+PREVWIND previous window
+GOSREC Gossling style recenter
+STARTUPFILE (unix & OSK) system-wide startup file
+XKEYS (Termcap) Put kepad in alternate mode, use
+ terminal-dependent startup file.
+
+Features removeable to save space
+
+NO_HELP help, descibe-bindings, describe-key-briefly, apropos
+NO_MACRO keyboard macros. If defined, NO_STARTUP must be also.
+NO_STARTUP startup files, load, etc.
+NO_BACKUP backup files when writing
+NO_DPROMPT Delayed prompt on multi-key sequences
+NO_DIR Dir change functions. If defined, NO_DIRED must be also.
+NO_DIRED Dired mode
+REGEX Regular expressions. Not default, since the code is rather
+ unportable and has the GNU copywrite on it.
+
+System dependant garbage (avoid where practical)
+
+VMS VMS
+AMIGA AMIGA
+
+
+Things that may be defined in system dependant code:
+
+XCHAR XCHAR and XSHORT (char and short for space savings)
+BDC2 more special characters for filenames
+BDC3 dito.
+METABIT Bit of KCHAR set on meta keys
+OFFSET macro to calculate offset of member from start of structure
+NBLOCK line growth amount
+KBLOCK kill buffer growth amount
+MALLOCROUND macro to predict malloc allocations stratagy
+SYSINIT system dependant initialization
+NO_VOID_TYPE compiler dosen't have type void
+ZEROARRAY zero length arrays are allowed
+BINDKEY include bindkey routine for use by system & terminal
+ dependent code.
+
+Terminal dependant
+
+DO_METAKEY meta key
+METABIT Which bit in a KCHAR is used by the meta key (default 0x80)
+STANDOUT_GLITCH standout (may) take character position(s)
+GOSLING optimize redisplay
+MEMMAP memory mapped display
+MOVE_STANDOUT cursor addressing may be done in standout mode
+FKEYS function keys do not fit in type char.
+ Not for use where function keys send multiple characters.
diff --git a/usr.bin/mg/modes.c b/usr.bin/mg/modes.c
new file mode 100644
index 00000000000..49144f2fd6f
--- /dev/null
+++ b/usr.bin/mg/modes.c
@@ -0,0 +1,138 @@
+/*
+ * Commands to toggle modes. Without an argument, toggle mode.
+ * Negitive or zero argument, mode off. Positive argument, mode on.
+ */
+
+#include "def.h"
+#include "kbd.h"
+
+int defb_nmodes = 0;
+MAPS *defb_modes[PBMODES] = {&map_table[0]};
+int defb_flag = 0;
+
+static int changemode(f, n, mode)
+int f, n;
+char *mode;
+{
+ register int i;
+ MAPS *m;
+ VOID upmodes();
+
+ if((m = name_mode(mode)) == NULL) {
+ ewprintf("Can't find mode %s", mode);
+ return FALSE;
+ }
+ if(!(f & FFARG)) {
+ for(i=0; i <= curbp->b_nmodes; i++)
+ if(curbp->b_modes[i] == m) {
+ n = 0; /* mode already set */
+ break;
+ }
+ }
+ if(n > 0) {
+ for(i=0; i <= curbp->b_nmodes; i++)
+ if(curbp->b_modes[i] == m) return TRUE; /* mode already set */
+ if(curbp->b_nmodes >= PBMODES-1) {
+ 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;
+}
+
+indentmode(f, n)
+{
+ return changemode(f, n, "indent");
+}
+
+fillmode(f, n)
+{
+ return changemode(f, n, "fill");
+}
+
+/*
+ * Fake the GNU "blink-matching-paren" variable.
+ */
+blinkparen(f, n)
+{
+ return changemode(f, n, "blink");
+}
+
+#ifdef NOTAB
+notabmode(f, 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
+
+overwrite(f, n)
+int f, 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;
+}
+
+set_default_mode(f, n)
+int f, n;
+{
+ register int i;
+ register MAPS *m;
+ char mode[32];
+
+ if(eread("Set Default Mode: ", mode, 32, EFNEW) != TRUE)
+ return ABORT;
+ if((m = name_mode(mode)) == NULL) {
+ ewprintf("can't find mode %s", mode);
+ return FALSE;
+ }
+ if(!(f & FFARG)) {
+ for(i=0; i <= defb_nmodes; i++)
+ if(defb_modes[i] == m) {
+ n = 0; /* mode already set */
+ break;
+ }
+ }
+ if(n > 0) {
+ for(i=0; i <= defb_nmodes; i++)
+ if(defb_modes[i] == m) return TRUE; /* mode already set */
+ if(defb_nmodes >= PBMODES-1) {
+ 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) return TRUE; /* mode wasn't set */
+ for(; i < defb_nmodes; i++)
+ defb_modes[i] = defb_modes[i+1];
+ defb_nmodes--;
+ }
+ if(strcmp(mode, "overwrite")==0)
+ if(n<=0) defb_flag &= ~BFOVERWRITE;
+ else defb_flag |= BFOVERWRITE;
+#ifdef NOTAB
+ if(strcmp(mode, "notab")==0)
+ if(n<=0) defb_flag &= ~BFNOTAB;
+ else defb_flag |= BFNOTAB;
+#endif
+ return TRUE;
+}
diff --git a/usr.bin/mg/paragraph.c b/usr.bin/mg/paragraph.c
new file mode 100644
index 00000000000..b09aa7810f3
--- /dev/null
+++ b/usr.bin/mg/paragraph.c
@@ -0,0 +1,284 @@
+/*
+ * 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 "def.h"
+
+static int fillcol = 70 ;
+#define MAXWORD 256
+
+/*
+ * go back to the begining of the current paragraph
+ * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
+ * combination to delimit the begining of a paragraph
+ */
+/*ARGSUSED*/
+gotobop(f, n)
+{
+ if (n < 0) /* the other way...*/
+ return gotoeop(f, -n);
+
+ while (n-- > 0) { /* for each one asked for */
+
+ /* first scan back until we are in a word */
+
+ while (backchar(FFRAND, 1) && !inword()) {}
+ curwp->w_doto = 0; /* and go to the B-O-Line */
+
+ /* and scan back until we hit a <NL><SP> <NL><TAB> or <NL><NL> */
+ while (lback(curwp->w_dotp) != curbp->b_linep)
+ if (llength(lback(curwp->w_dotp))
+ && lgetc(curwp->w_dotp,0) != ' '
+ && lgetc(curwp->w_dotp,0) != '.'
+ && lgetc(curwp->w_dotp,0) != '\t')
+ curwp->w_dotp = lback(curwp->w_dotp);
+ else {
+ if (llength(lback(curwp->w_dotp))
+ && lgetc(curwp->w_dotp,0) == '.') {
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ if(curwp->w_dotp == curbp->b_linep) {
+ /* beond end of buffer, cleanup time */
+ curwp->w_dotp = lback(curwp->w_dotp);
+ curwp->w_doto = llength(curwp->w_dotp);
+ }
+ }
+ break;
+ }
+ }
+ curwp->w_flag |= WFMOVE; /* force screen update */
+ return TRUE;
+}
+
+/*
+ * go forword to the end of the current paragraph
+ * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
+ * combination to delimit the begining of a paragraph
+ */
+/*ARGSUSED*/
+gotoeop(f, n)
+{
+ if (n < 0) /* the other way...*/
+ return gotobop(f, -n);
+
+ while (n-- > 0) { /* for each one asked for */
+
+ /* Find the first word on/after the current line */
+ curwp->w_doto = 0;
+ while(forwchar(FFRAND, 1) && !inword()) {}
+ curwp->w_doto = 0;
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ /* and scan forword until we hit a <NL><SP> or ... */
+ while (curwp->w_dotp != curbp->b_linep) {
+ if (llength(curwp->w_dotp)
+ && lgetc(curwp->w_dotp,0) != ' '
+ && lgetc(curwp->w_dotp,0) != '.'
+ && lgetc(curwp->w_dotp,0) != '\t')
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ else
+ break;
+ }
+ if(curwp->w_dotp == curbp->b_linep) {
+ /* beond end of buffer, cleanup time */
+ curwp->w_dotp = lback(curwp->w_dotp);
+ curwp->w_doto = llength(curwp->w_dotp);
+ break;
+ }
+ }
+ curwp->w_flag |= WFMOVE; /* force screen update */
+ return TRUE;
+}
+
+/*
+ * Fill the current paragraph according to the current
+ * fill column
+ */
+/*ARGSUSED*/
+fillpara(f, n)
+{
+ register int c; /* current char durring scan */
+ register int wordlen; /* length of current word */
+ register int clength; /* position on line during fill */
+ register int i; /* index during word copy */
+ register 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 */
+ LINE *eopline; /* pointer to line just past EOP */
+ char wbuf[MAXWORD]; /* buffer for current word */
+
+ /* 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 begining of the paragraph */
+ (VOID) gotobop(FFRAND, 1);
+
+ /* initialize various info */
+ while (!inword() && 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)
+ return FALSE;
+
+ /* if not a separator, just add it in */
+ if (c != ' ' && c != '\t') {
+ if (wordlen < MAXWORD - 1)
+ wbuf[wordlen++] = c;
+ else {
+ /* You loose chars beyond MAXWORD if the word
+ * is to long. I'm to 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 tenatitive 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.
+ */
+ if((eolflag || curwp->w_doto==llength(curwp->w_dotp)
+ || (c=lgetc(curwp->w_dotp,curwp->w_doto))==' '
+ || c=='\t')
+ && ISEOSP(wbuf[wordlen-1])
+ && 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 realy 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
+ * begining of the blank line. */
+ (VOID) backchar(FFRAND, 1);
+ return TRUE;
+}
+
+/* delete n paragraphs starting with the current one */
+/*ARGSUSED*/
+killpara(f, n)
+{
+ register int status; /* returned status of functions */
+
+ while (n--) { /* for each paragraph to delete */
+
+ /* mark out the end and begining of the para to delete */
+ (VOID) gotoeop(FFRAND, 1);
+
+ /* set the mark here */
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+
+ /* go to the begining of the paragraph */
+ (VOID) gotobop(FFRAND, 1);
+ curwp->w_doto = 0; /* force us to the begining of line */
+
+ /* and delete it */
+ if ((status = killregion(FFRAND, 1)) != TRUE)
+ return status;
+
+ /* and clean up the 2 extra lines */
+ (VOID) ldelete((RSIZE) 1, KFORW);
+ }
+ return TRUE;
+}
+
+/*
+ * check to see if we're past fillcol, and if so,
+ * justify this line. As a last step, justify the line.
+ */
+/*ARGSUSED*/
+fillword(f, n)
+{
+ register char c;
+ register 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_flag |= WFMOVE;
+ if (nce == 0 && curwp->w_doto != 0) return fillword(f, n);
+ return TRUE;
+}
+
+/* Set fill column to n. */
+setfillcol(f, n) {
+ extern int getcolpos() ;
+
+ fillcol = ((f & FFARG) ? n : getcolpos());
+ ewprintf("Fill column set to %d", fillcol);
+ return TRUE;
+}
diff --git a/usr.bin/mg/random.c b/usr.bin/mg/random.c
new file mode 100644
index 00000000000..297de45eba5
--- /dev/null
+++ b/usr.bin/mg/random.c
@@ -0,0 +1,444 @@
+/*
+ * Assorted commands.
+ * The 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 "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. 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*/
+showcpos(f, n)
+{
+ register LINE *clp;
+ register long nchar;
+ long cchar;
+ register int nline, row;
+ int cline, cbyte; /* Current line/char/byte */
+ int ratio;
+
+ clp = lforw(curbp->b_linep); /* Collect the data. */
+ nchar = 0;
+ nline = 0;
+ for (;;) {
+ ++nline; /* Count this line */
+ if (clp == curwp->w_dotp) {
+ cline = nline; /* Mark line */
+ cchar = nchar + curwp->w_doto;
+ if (curwp->w_doto == llength(clp))
+ cbyte = '\n';
+ else
+ cbyte = lgetc(clp, curwp->w_doto);
+ }
+ nchar += llength(clp); /* Now count the chars */
+ clp = lforw(clp);
+ if (clp == curbp->b_linep) break;
+ nchar++; /* count the newline */
+ }
+ row = curwp->w_toprow + 1; /* Determine row. */
+ clp = curwp->w_linep;
+ while (clp!=curbp->b_linep && clp!=curwp->w_dotp) {
+ ++row;
+ clp = lforw(clp);
+ }
+ /*NOSTRICT*/
+ ratio = nchar ? (100L*cchar) / nchar : 100;
+ ewprintf("Char: %c (0%o) point=%ld(%d%%) line=%d row=%d col=%d",
+ cbyte, cbyte, cchar, ratio, cline, row, getcolpos());
+ return TRUE;
+}
+
+getcolpos() {
+ register int col, i, c;
+
+ col = 1; /* Determine column. */
+ for (i=0; i<curwp->w_doto; ++i) {
+ c = lgetc(curwp->w_dotp, i);
+ if (c == '\t'
+#ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+#endif
+ ) {
+ col |= 0x07;
+ ++col;
+ } else if (ISCTRL(c) != FALSE)
+ ++col;
+ ++col;
+ }
+ return col;
+}
+/*
+ * Twiddle the two characters on either side of
+ * dot. If dot is at the end of the line twiddle the
+ * two characters before it. Return with an error if dot
+ * is at the beginning of line; it seems to be a bit
+ * pointless to make this work. This fixes up a very
+ * common typo with a single stroke. Normally bound
+ * to "C-T". This always works within a line, so
+ * "WFEDIT" is good enough.
+ */
+/*ARGSUSED*/
+twiddle(f, n)
+{
+ register LINE *dotp;
+ register int doto;
+ register int cr;
+ VOID lchange();
+
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+ if(doto==llength(dotp)) {
+ if(--doto<=0) return FALSE;
+ } else {
+ if(doto==0) return FALSE;
+ ++curwp->w_doto;
+ }
+ cr = lgetc(dotp, doto--);
+ lputc(dotp, doto+1, lgetc(dotp, doto));
+ lputc(dotp, doto, cr);
+ 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
+ * procerssors. They even handle the looping. Normally
+ * this is bound to "C-O".
+ */
+/*ARGSUSED*/
+openline(f, n)
+{
+ register int i;
+ register int s;
+
+ if (n < 0)
+ return FALSE;
+ if (n == 0)
+ return TRUE;
+ i = n; /* Insert newlines. */
+ do {
+ s = lnewline();
+ } while (s==TRUE && --i);
+ if (s == TRUE) /* Then back up overtop */
+ s = backchar(f | FFRAND, n); /* of them all. */
+ return s;
+}
+
+/*
+ * Insert a newline.
+ * [following "feature" not present in current version of
+ * Gnu, and now disabled here too]
+ * If you are at the end of the line and the
+ * next line is a blank line, just move into the
+ * blank line. This makes "C-O" and "C-X C-O" work
+ * nicely, and reduces the ammount of screen
+ * update that has to be done. This would not be
+ * as critical if screen update were a lot
+ * more efficient.
+ */
+/*ARGSUSED*/
+newline(f, n)
+{
+ register LINE *lp;
+ register int s;
+
+ if (n < 0) return FALSE;
+ while (n--) {
+ lp = curwp->w_dotp;
+#ifdef undef
+ if (llength(lp) == curwp->w_doto
+ && lforw(lp) != curbp->b_linep
+ && llength(lforw(lp)) == 0) {
+ if ((s=forwchar(FFRAND, 1)) != TRUE)
+ return s;
+ } else
+#endif
+ 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*/
+deblank(f, n)
+{
+ register LINE *lp1;
+ register LINE *lp2;
+ register RSIZE nld;
+
+ lp1 = curwp->w_dotp;
+ while (llength(lp1)==0 && (lp2=lback(lp1))!=curbp->b_linep)
+ lp1 = lp2;
+ lp2 = lp1;
+ nld = (RSIZE) 0;
+ while ((lp2=lforw(lp2))!=curbp->b_linep && 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.
+ */
+justone(f, n) {
+ (VOID) delwhite(f, n);
+ return linsert(1, ' ');
+}
+/*
+ * Delete any whitespace around dot.
+ */
+/*ARGSUSED*/
+delwhite(f, n)
+{
+ register int col, c, s;
+
+ col = curwp->w_doto;
+ while (((c = lgetc(curwp->w_dotp, col)) == ' ' || c == '\t')
+ && col < llength(curwp->w_dotp))
+ ++col;
+ do {
+ if (curwp->w_doto == 0) {
+ s = FALSE;
+ break;
+ }
+ if ((s = backchar(FFRAND, 1)) != TRUE) break;
+ } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ' || c == '\t');
+
+ if (s == TRUE) (VOID) forwchar(FFRAND, 1);
+ (VOID) ldelete((RSIZE)(col - curwp->w_doto), KNONE);
+ 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 subcomands failed. Normally bound
+ * to "C-J".
+ */
+/*ARGSUSED*/
+indent(f, n)
+{
+ register int nicol;
+ register int c;
+ register int i;
+
+ if (n < 0) return (FALSE);
+ 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
+ ((i=nicol/8)!=0 && linsert(i, '\t')==FALSE) ||
+ ((i=nicol%8)!=0 && linsert(i, ' ')==FALSE))))
+ return FALSE;
+ }
+ 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*/
+forwdel(f, n)
+{
+ if (n < 0)
+ return backdel(f | FFRAND, -n);
+ if (f & FFARG) { /* Really a kill. */
+ 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*/
+backdel(f, n)
+{
+ register int s;
+
+ if (n < 0)
+ return forwdel(f | FFRAND, -n);
+ if (f & FFARG) { /* Really a kill. */
+ if ((lastflag&CFKILL) == 0)
+ kdelete();
+ thisflag |= CFKILL;
+ }
+ if ((s=backchar(f | FFRAND, n)) == TRUE)
+ s = ldelete((RSIZE) n, (f & FFARG) ? KFORW : KNONE);
+ return s;
+}
+
+/*
+ * 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*/
+killline(f, n) {
+ register RSIZE chunk;
+ register LINE *nextp;
+ register int i, c;
+ VOID kdelete();
+
+ if ((lastflag&CFKILL) == 0) /* Clear kill buffer if */
+ kdelete(); /* last wasn't a kill. */
+ 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+1;
+ nextp = lforw(curwp->w_dotp);
+ i = n;
+ while (--i) {
+ if (nextp == curbp->b_linep)
+ break;
+ chunk += llength(nextp)+1;
+ nextp = lforw(nextp);
+ }
+ } else { /* n <= 0 */
+ chunk = curwp->w_doto;
+ curwp->w_doto = 0;
+ i = n;
+ while (i++) {
+ if (lback(curwp->w_dotp) == curbp->b_linep)
+ break;
+ curwp->w_dotp = lback(curwp->w_dotp);
+ curwp->w_flag |= WFMOVE;
+ chunk += llength(curwp->w_dotp)+1;
+ }
+ }
+ /*
+ * KFORW here is a bug. Should be KBACK/KFORW, but we need to
+ * rewrite the ldelete code (later)?
+ */
+ return (ldelete(chunk, KFORW));
+}
+
+/*
+ * 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*/
+yank(f, n)
+{
+ register int c;
+ register int i;
+ register LINE *lp;
+ register int nline;
+ VOID isetmark();
+
+ if (n < 0) return FALSE;
+ nline = 0; /* Newline counting. */
+ while (n--) {
+ isetmark(); /* mark around last yank */
+ i = 0;
+ while ((c=kremove(i)) >= 0) {
+ if (c == '\n') {
+ if (newline(FFRAND, 1) == FALSE)
+ return FALSE;
+ ++nline;
+ } else {
+ if (linsert(1, c) == FALSE)
+ return FALSE;
+ }
+ ++i;
+ }
+ }
+ lp = curwp->w_linep; /* Cosmetic adjustment */
+ if (curwp->w_dotp == lp) { /* if offscreen insert. */
+ while (nline-- && lback(lp)!=curbp->b_linep)
+ lp = lback(lp);
+ curwp->w_linep = lp; /* Adjust framing. */
+ curwp->w_flag |= WFHARD;
+ }
+ return TRUE;
+}
+
+#ifdef NOTAB
+/*ARGSUSED*/
+space_to_tabstop(f, n)
+int f, n;
+{
+ if(n<0) return FALSE;
+ if(n==0) return TRUE;
+ return linsert((n<<3) - (curwp->w_doto & 7), ' ');
+}
+#endif
diff --git a/usr.bin/mg/re_search.c b/usr.bin/mg/re_search.c
new file mode 100644
index 00000000000..010cb6024e5
--- /dev/null
+++ b/usr.bin/mg/re_search.c
@@ -0,0 +1,706 @@
+/*
+ * regular expression search commands for
+ * MicroGnuEmacs
+ *
+ * This file contains functions to implement several of gnuemacs'
+ * regular expression functions for MicroGnuEmacs. Several of
+ * the routines below are just minor rearrangements of the MicroGnuEmacs
+ * non-regular expression search functions. Hence some of them date back
+ * in essential structure to the original MicroEMACS; others are modifications
+ * of Rich Ellison's code. I, Peter Newton, wrote about half from scratch.
+ *
+ * Although I have nothing to do with the GNU project, these functions
+ * require the GNU project's regular expression package (files regex.c and
+ * regex.h). Hence, this file comes under the same copyright notice
+ * as the GNU project's code. As far as I know, the rest of MicroGnuEmacs
+ * need not since it may be used independently of any GNU project code. In
+ * any case, I certainly do not warrant either the correctness or utility
+ * of this code. The GNU project copyright notice follows. Don't you
+ * wish they would make it a bit shorter!
+ */
+
+/*
+GNU Emacs copying permission notice Copyright (C) 1985 Richard M. Stallman
+ Verbatim copies of this document, including its copyright notice,
+ may be distributed by anyone in any manner.
+ Distribution with modifications is not permitted.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but without any warranty. No author or distributor
+accepts responsibility to anyone for the consequences of using it
+or for whether it serves any particular purpose or works at all,
+unless he says so in writing.
+
+Everyone is granted permission to copy, modify and redistribute
+GNU Emacs under the following conditions:
+
+ Permission is granted to anyone to make or distribute verbatim copies
+ of GNU Emacs source code as received, in any medium, provided that all
+ copyright notices and permission and nonwarranty notices are preserved,
+ and that the distributor grants the recipient permission
+ for further redistribution as permitted by this document,
+ and gives him and points out to him an exact copy of this document
+ to inform him of his rights.
+
+ Permission is granted to distribute modified versions
+ of GNU Emacs source code, or of portions of it,
+ under the above conditions, provided also that all
+ changed files carry prominent notices stating who last changed them
+ and that all the GNU-Emacs-derived material, including everything
+ packaged together with it and not independently usable, is
+ distributed under the conditions stated in this document.
+
+ Permission is granted to distribute GNU Emacs in
+ compiled or executable form under the same conditions applying
+ for source code, provided that either
+ A. it is accompanied by the corresponding machine-readable
+ source code, or
+ B. it is accompanied by a written offer, with no time limit,
+ to give anyone a machine-readable copy of the corresponding
+ source code in return for reimbursement of the cost of distribution.
+ This written offer must permit verbatim duplication by anyone.
+ C. it is distributed by someone who received only the
+ executable form, and is accompanied by a copy of the
+ written offer of source code which he received along with it.
+
+In other words, you are welcome to use, share and improve GNU Emacs
+You are forbidden to forbid anyone else to use, share and improve
+what you give them. Help stamp out software-hoarding!
+*/
+
+#ifdef REGEX
+#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)
+
+char re_pat[NPAT]; /* Regex pattern */
+int re_srch_lastdir = SRCH_NOPR; /* Last search flags. */
+int casefoldsearch = TRUE; /* Does search ignore case ? */
+
+/* Indexed by a character, gives the upper case equivalent of the character */
+
+static char upcase[0400] =
+ { 000, 001, 002, 003, 004, 005, 006, 007,
+ 010, 011, 012, 013, 014, 015, 016, 017,
+ 020, 021, 022, 023, 024, 025, 026, 027,
+ 030, 031, 032, 033, 034, 035, 036, 037,
+ 040, 041, 042, 043, 044, 045, 046, 047,
+ 050, 051, 052, 053, 054, 055, 056, 057,
+ 060, 061, 062, 063, 064, 065, 066, 067,
+ 070, 071, 072, 073, 074, 075, 076, 077,
+ 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
+ };
+
+/*
+ * 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*/
+re_forwsearch(f, n) {
+ register int s;
+
+ if ((s=re_readpattern("RE Search")) != TRUE)
+ return (s);
+ if (re_forwsrch() == FALSE) {
+ 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*/
+re_backsearch(f, n) {
+ register int s;
+
+ if ((s=re_readpattern("RE Search backward")) != TRUE)
+ return (s);
+ if (re_backsrch() == FALSE) {
+ 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.
+ */
+/*ARGSUSED*/
+/* This code has problems-- some incompatibility(?) with
+ extend.c causes match to fail when it should not.
+ */
+re_searchagain(f, n) {
+
+ if (re_srch_lastdir == SRCH_NOPR) {
+ ewprintf("No last search");
+ return (FALSE);
+ }
+
+ if (re_srch_lastdir == SRCH_FORW) {
+ if (re_forwsrch() == FALSE) {
+ ewprintf("Search failed: \"%s\"", re_pat);
+ return (FALSE);
+ }
+ return (TRUE);
+ }
+ if (re_srch_lastdir == SRCH_BACK) {
+ if (re_backsrch() == FALSE) {
+ ewprintf("Search failed: \"%s\"", re_pat);
+ return (FALSE);
+ }
+ return (TRUE);
+ }
+}
+
+
+#include "regex.h"
+#define BYTEWIDTH 8
+
+/* Compiled regex goes here-- changed only when new pattern read */
+static struct re_pattern_buffer re_buff;
+static char fastmap[(1 << BYTEWIDTH)];
+
+/* regs holds boundaries of matched text */
+static struct re_registers regs;
+
+/*
+ * Re-Query Replace.
+ * Replace strings selectively. Does a search and replace operation.
+ */
+/*ARGSUSED*/
+re_queryrepl(f, n) {
+ register int s;
+ register int rcnt = 0; /* Replacements made so far */
+ register int plen; /* length of found string */
+ char news[NPAT]; /* replacement string */
+
+ /* Casefold check */
+ if (!casefoldsearch) f = TRUE;
+
+ if ((s=re_readpattern("RE Query replace")) != TRUE)
+ return (s);
+ if ((s=ereply("Query replace %s with: ",news, NPAT, re_pat)) == ABORT)
+ return (s);
+ if (s == FALSE)
+ news[0] = '\0';
+ 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();
+ switch (getkey(FALSE)) {
+ case ' ':
+ plen = regs.end[0] - regs.start[0];
+ if (re_doreplace((RSIZE) plen, news, f) == FALSE)
+ return (FALSE);
+ rcnt++;
+ break;
+
+ case '.':
+ plen = regs.end[0] - regs.start[0];
+ if (re_doreplace((RSIZE) plen, news, f) == FALSE)
+ return (FALSE);
+ rcnt++;
+ goto stopsearch;
+
+ case CCHR('G'): /* ^G */
+ (VOID) ctrlg(FFRAND, 0);
+ case CCHR('['): /* ESC */
+ case '`':
+ goto stopsearch;
+
+ case '!':
+ do {
+ plen = regs.end[0] - regs.start[0];
+ if (re_doreplace((RSIZE) plen, news, f) == 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_flag |= WFHARD;
+ update();
+ 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;
+}
+
+
+
+/* Routine re_doreplace calls lreplace to make replacements needed by
+ * re_query replace. Its reason for existence is to deal with \1,
+ * \2. etc.
+ */
+
+/* Maximum length of replacement string */
+#define REPLEN 256
+
+re_doreplace(plen, st, f)
+ register RSIZE plen; /* length to remove */
+ char *st; /* replacement string */
+ int f; /* case hack disable */
+{
+ int s;
+ int num, k;
+ register int j;
+ int more, state;
+ LINE *clp;
+ char repstr[REPLEN];
+
+ clp = curwp->w_dotp;
+ more = TRUE;
+ j = 0;
+ state = 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_NREGS) return(FALSE);
+ k = regs.end[num] - regs.start[num];
+ if (j+k >= REPLEN) return(FALSE);
+ bcopy(&(clp->l_text[regs.start[num]]), &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;
+ } /* end case */
+ } /* end while */
+
+ repstr[j] = '\0';
+
+ s = lreplace(plen, repstr, f);
+
+ 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.
+ */
+re_forwsrch() {
+
+ register LINE *clp;
+ register int tbo;
+ int ntries;
+ int i, plen;
+
+ clp = curwp->w_dotp;
+ tbo = curwp->w_doto;
+
+ if (tbo == clp->l_used)
+ /* Don't start matching off end of line-- must
+ * move to beginning of next line, unless at end
+ */
+ if (clp != curbp->b_linep) {
+ clp = lforw(clp);
+ 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_linep)) {
+
+ ntries = llength(clp) - tbo;
+ i = re_search (&re_buff, ltext(clp), llength(clp), tbo, ntries, &regs);
+
+ if (i == -1) {
+ clp = lforw(clp);
+ tbo = 0;
+ }
+ else {
+ curwp->w_doto = regs.end[0];
+ curwp->w_dotp = clp;
+ curwp->w_flag |= 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.
+ */
+re_backsrch() {
+
+ register LINE *clp;
+ register int tbo;
+ int ntries;
+ int i, startpos;
+char m[1];
+
+ clp = curwp->w_dotp;
+ tbo = curwp->w_doto;
+
+ /* Start search one position to the left of dot */
+ tbo = tbo - 1;
+ if (tbo < 0) {
+ /* must move up one line */
+ clp = lback(clp);
+ 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_linep)) {
+
+ ntries = tbo;
+ i = re_search (&re_buff, ltext(clp), llength(clp), tbo, -ntries, &regs);
+
+ if (i == -1) {
+ clp = lback(clp);
+ tbo = llength(clp);
+ }
+ else {
+ curwp->w_doto = regs.start[0];
+ curwp->w_dotp = clp;
+ curwp->w_flag |= 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.
+ */
+re_readpattern(prompt) char *prompt; {
+ register int s;
+ char tpat[NPAT];
+ char *message;
+
+ if (re_pat[0] == '\0') s = ereply("%s: ", tpat, NPAT, prompt);
+ else s = ereply("%s: (default %s) ", tpat, NPAT, prompt, re_pat);
+
+ if (s == TRUE) {
+ /* New pattern given */
+ (VOID) strcpy(re_pat, tpat);
+ re_buff.allocated = 40;
+ re_buff.buffer = (char *) malloc (re_buff.allocated);
+ re_buff.fastmap = fastmap;
+ if (casefoldsearch)
+ re_buff.translate = upcase;
+ else
+ re_buff.translate = '\0';
+ message = re_compile_pattern (re_pat, strlen(re_pat), &re_buff);
+ if (message != '\0') {
+ ewprintf("Regex Error: %s", message);
+ re_pat[0] = '\0';
+ return(FALSE);
+ }
+ re_compile_fastmap (&re_buff);
+ }
+ else if (s==FALSE && re_pat[0]!='\0')
+ /* Just using old pattern */
+ s = TRUE;
+ return (s);
+}
+
+
+
+/* Cause case to not matter in searches. This is the default. If
+ * called with argument cause case to matter.
+ */
+setcasefold(f, 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);
+
+} /* end setcasefold */
+
+
+/* Delete all lines after dot that contain a string matching regex
+ */
+delmatchlines(f, 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
+ */
+delnonmatchlines(f, 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 */
+killmatches(cond)
+ int cond;
+{
+ int s, i;
+ int count = 0;
+ LINE *clp;
+
+ clp = curwp->w_dotp;
+ if (curwp->w_doto == llength(clp))
+ /* Consider dot on next line */
+ clp = lforw(clp);
+
+ while (clp != (curbp->b_linep)) {
+
+ /* see if line matches */
+ i = re_search (&re_buff, ltext(clp), llength(clp), 0, llength(clp),
+ &regs);
+ /* Delete line when appropriate */
+ if ((cond == FALSE && i == -1) || (cond == TRUE && i != -1)) {
+ curwp->w_doto = 0;
+ curwp->w_dotp = clp;
+ count++;
+ s = ldelete(llength(clp)+1, KNONE);
+ clp = curwp->w_dotp;
+ curwp->w_flag |= WFMOVE;
+ if (s == FALSE) return(FALSE);
+ }
+ else
+ clp = lforw(clp);
+ }
+
+ ewprintf("%d line(s) deleted", count);
+ if (count > 0) curwp->w_flag |= WFMOVE;
+
+ return(TRUE);
+}
+
+
+petersfunc(f, n) {
+
+ int s;
+ LINE *clp;
+ char c;
+
+ curwp->w_doto = 0;
+ s = ldelete(llength(curwp->w_dotp)+1, KNONE);
+ curwp->w_flag |= WFMOVE;
+ return(s);
+
+}
+
+
+/* Count lines matching regex
+ */
+cntmatchlines(f, 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
+ */
+cntnonmatchlines(f, 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 */
+countmatches(cond)
+ int cond;
+{
+ int s, i;
+ int count = 0;
+ LINE *clp;
+
+ clp = curwp->w_dotp;
+ if (curwp->w_doto == llength(clp))
+ /* Consider dot on next line */
+ clp = lforw(clp);
+
+ while (clp != (curbp->b_linep)) {
+
+ /* see if line matches */
+ i = re_search (&re_buff, ltext(clp), llength(clp), 0, llength(clp),
+ &regs);
+ /* Count line when appropriate */
+ if ((cond == FALSE && i == -1) || (cond == TRUE && i != -1)) 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
diff --git a/usr.bin/mg/regex.c b/usr.bin/mg/regex.c
new file mode 100644
index 00000000000..817e533ce8e
--- /dev/null
+++ b/usr.bin/mg/regex.c
@@ -0,0 +1,1594 @@
+/* Imagine Generic gnuemacs copyright notice here */
+
+/* modified by Robert Larson to make fewer assumptions about the compiler */
+/* (Making it useful to mg) (#if not supported by osk, enum not supported by many) */
+
+/* To test, compile with -Dtest.
+ This Dtestable feature turns this into a self-contained program
+ which reads a pattern, describes how it compiles,
+ then reads a string and searches for it. */
+
+
+#ifdef REGEX
+#include "def.h" /* defines VOID etc. for mg */
+
+#ifdef emacs
+
+/* The `emacs' switch turns on certain special matching commands
+ that make sense only in emacs. */
+
+#include "config.h"
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+#else /* not emacs */
+
+/*
+ * Define the syntax stuff, so we can do the \<...\> things.
+ */
+
+#ifndef Sword /* must be non-zero in some of the tests below... */
+#define Sword 1
+#endif
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#ifdef SYNTAX_TABLE
+
+char *re_syntax_table;
+
+#else
+
+static char re_syntax_table[256];
+
+static VOID
+init_syntax_once ()
+{
+ register int c;
+ static int done = 0;
+
+ if (done)
+ return;
+
+ bzero (re_syntax_table, sizeof re_syntax_table);
+
+ for (c = 'a'; c <= 'z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = 'A'; c <= 'Z'; c++)
+ re_syntax_table[c] = Sword;
+
+ for (c = '0'; c <= '9'; c++)
+ re_syntax_table[c] = Sword;
+
+ done = 1;
+}
+
+#endif /* SYNTAX_TABLE */
+#endif /* not emacs */
+
+#include "regex.h"
+
+/* Number of failure points to allocate space for initially,
+ when matching. If this number is exceeded, more space is allocated,
+ so it is not a hard limit. */
+
+#ifndef NFAILURES
+#define NFAILURES 80
+#endif NFAILURES
+
+/* width of a byte in bits */
+
+#define BYTEWIDTH 8
+
+/* These are the command codes that appear in compiled regular expressions, one per byte.
+ Some command codes are followed by argument bytes.
+ A command code can specify any interpretation whatever for its arguments.
+ Zero-bytes may appear in the compiled regular expression. */
+
+typedef char regexpcode;
+#define unused 0
+#define exactn 1 /* followed by one byte giving n, and then by n literal bytes */
+#define begline 2 /* fails unless at beginning of line */
+#define endline 3 /* fails unless at end of line */
+#define jump 4 /* followed by two bytes giving relative address to jump to */
+#define on_failure_jump 5 /* followed by two bytes giving relative address of place */
+ /* to resume at in case of failure. */
+#define finalize_jump 6 /* Throw away latest failure point and then jump to address. */
+#define maybe_finalize_jump 7 /* Like jump but finalize if safe to do so. */
+ /* This is used to jump back to the beginning
+ of a repeat. If the command that follows
+ this jump is clearly incompatible with the
+ one at the beginning of the repeat, such that
+ we can be sure that there is no use backtracking
+ out of repetitions already completed,
+ then we finalize. */
+#define dummy_failure_jump 8 /* jump, and push a dummy failure point. */
+ /* This failure point will be thrown away
+ if an attempt is made to use it for a failure.
+ A + construct makes this before the first repeat. */
+#define anychar 9 /* matches any one character */
+#define charset 10 /* matches any one char belonging to specified set. */
+ /* First following byte is # bitmap bytes.
+ Then come bytes for a bit-map saying which chars are in.
+ Bits in each byte are ordered low-bit-first.
+ A character is in the set if its bit is 1.
+ A character too large to have a bit in the map
+ is automatically not in the set */
+#define charset_not 11 /* similar but match any character that is NOT one of those specified */
+#define start_memory 12 /* starts remembering the text that is matched */
+ /* and stores it in a memory register.
+ followed by one byte containing the register number.
+ Register numbers must be in the range 0 through NREGS. */
+#define stop_memory 13 /* stops remembering the text that is matched */
+ /* and stores it in a memory register.
+ followed by one byte containing the register number.
+ Register numbers must be in the range 0 through NREGS. */
+#define duplicate 14 /* match a duplicate of something remembered. */
+ /* Followed by one byte containing the index of the memory register. */
+#define before_dot 15 /* Succeeds if before dot */
+#define at_dot 16 /* Succeeds if at dot */
+#define after_dot 17 /* Succeeds if after dot */
+#define begbuf 18 /* Succeeds if at beginning of buffer */
+#define endbuf 19 /* Succeeds if at end of buffer */
+#define wordchar 20 /* Matches any word-constituent character */
+#define notwordchar 21 /* Matches any char that is not a word-constituent */
+#define wordbeg 22 /* Succeeds if at word beginning */
+#define wordend 23 /* Succeeds if at word end */
+#define wordbound 24 /* Succeeds if at a word boundary */
+#define notwordbound 25 /* Succeeds if not at a word boundary */
+#define syntaxspec 26 /* Matches any character whose syntax is specified. */
+ /* followed by a byte which contains a syntax code, Sword or such like */
+#define notsyntaxspec 27 /* Matches any character whose syntax differs from the specified. */
+
+
+#ifndef SIGN_EXTEND_CHAR
+#define SIGN_EXTEND_CHAR(x) (x)
+#endif
+
+/* compile_pattern takes a regular-expression descriptor string in the user's format
+ and converts it into a buffer full of byte commands for matching.
+
+ pattern is the address of the pattern string
+ size is the length of it.
+ bufp is a struct re_pattern_buffer * which points to the info
+ on where to store the byte commands.
+ This structure contains a char * which points to the
+ actual space, which should have been obtained with malloc.
+ compile_pattern may use realloc to grow the buffer space.
+
+ The number of bytes of commands can be found out by looking in
+ the struct re_pattern_buffer that bufp pointed to,
+ after compile_pattern returns.
+*/
+
+#define PATPUSH(ch) (*b++ = (char) (ch))
+
+#define PATFETCH(c) \
+ {if (p == pend) goto end_of_pattern; \
+ c = * (unsigned char *) p++; \
+ if (translate) c = translate[c]; }
+
+#define PATFETCH_RAW(c) \
+ {if (p == pend) goto end_of_pattern; \
+ c = * (unsigned char *) p++; }
+
+#define PATUNFETCH p--
+
+#define EXTEND_BUFFER \
+ { char *old_buffer = bufp->buffer; \
+ if (bufp->allocated == (1<<16)) goto too_big; \
+ bufp->allocated *= 2; \
+ if (bufp->allocated > (1<<16)) bufp->allocated = (1<<16); \
+ if (!(bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated))) \
+ goto memory_exhausted; \
+ c = bufp->buffer - old_buffer; \
+ b += c; \
+ if (fixup_jump) \
+ fixup_jump += c; \
+ if (laststart) \
+ laststart += c; \
+ begalt += c; \
+ if (pending_exact) \
+ pending_exact += c; \
+ }
+
+static int store_jump (), insert_jump ();
+
+char *
+re_compile_pattern (pattern, size, bufp)
+ char *pattern;
+ int size;
+ struct re_pattern_buffer *bufp;
+{
+ register char *b = bufp->buffer;
+ register char *p = pattern;
+ char *pend = pattern + size;
+ register unsigned c, c1;
+ char *p1;
+ unsigned char *translate = (unsigned char *) bufp->translate;
+
+ /* address of the count-byte of the most recently inserted "exactn" command.
+ This makes it possible to tell whether a new exact-match character
+ can be added to that command or requires a new "exactn" command. */
+
+ char *pending_exact = 0;
+
+ /* address of the place where a forward-jump should go
+ to the end of the containing expression.
+ Each alternative of an "or", except the last, ends with a forward-jump
+ of this sort. */
+
+ char *fixup_jump = 0;
+
+ /* address of start of the most recently finished expression.
+ This tells postfix * where to find the start of its operand. */
+
+ char *laststart = 0;
+
+ /* In processing a repeat, 1 means zero matches is allowed */
+
+ char zero_times_ok;
+
+ /* In processing a repeat, 1 means many matches is allowed */
+
+ char many_times_ok;
+
+ /* address of beginning of regexp, or inside of last \( */
+
+ char *begalt = b;
+
+ /* Stack of information saved by \( and restored by \).
+ Four stack elements are pushed by each \(:
+ First, the value of b.
+ Second, the value of fixup_jump.
+ Third, the value of regnum.
+ Fourth, the value of begalt. */
+
+ int stackb[40];
+ int *stackp = stackb;
+ int *stacke = stackb + 40;
+ int *stackt;
+
+ /* Counts \('s as they are encountered. Remembered for the matching \),
+ where it becomes the "register number" to put in the stop_memory command */
+
+ int regnum = 1;
+
+ bufp->fastmap_accurate = 0;
+
+#ifndef SYNTAX_TABLE
+#ifndef emacs
+ /*
+ * Initialize the syntax table.
+ */
+ init_syntax_once();
+#endif
+#endif
+
+ if (bufp->allocated == 0)
+ {
+ bufp->allocated = 28;
+ if (bufp->buffer)
+ /* EXTEND_BUFFER loses when bufp->allocated is 0 */
+ bufp->buffer = (char *) realloc (bufp->buffer, 28);
+ else
+ /* Caller did not allocate a buffer. Do it for him */
+ bufp->buffer = (char *) malloc (28);
+ if (!bufp->buffer) goto memory_exhausted;
+ begalt = b = bufp->buffer;
+ }
+
+ while (p != pend)
+ {
+ if (b - bufp->buffer > bufp->allocated - 10)
+ /* Note that EXTEND_BUFFER clobbers c */
+ EXTEND_BUFFER;
+
+ PATFETCH (c);
+
+ switch (c)
+ {
+ case '$':
+ /* $ means succeed if at end of line, but only in special contexts.
+ If randonly in the middle of a pattern, it is a normal character. */
+ if (p == pend || (*p == '\\' && (p[1] == ')' || p[1] == '|')))
+ {
+ PATPUSH (endline);
+ break;
+ }
+ goto normal_char;
+
+ case '^':
+ /* ^ means succeed if at beg of line, but only if no preceding pattern. */
+ if (laststart) goto normal_char;
+ PATPUSH (begline);
+ break;
+
+ case '*':
+ case '+':
+ case '?':
+ /* If there is no previous pattern, char not special. */
+ if (!laststart)
+ goto normal_char;
+ /* If there is a sequence of repetition chars,
+ collapse it down to equivalent to just one. */
+ zero_times_ok = 0;
+ many_times_ok = 0;
+ while (1)
+ {
+ zero_times_ok |= c != '+';
+ many_times_ok |= c != '?';
+ if (p == pend)
+ break;
+ PATFETCH (c);
+ if (!(c == '*' || c == '+' || c == '?'))
+ {
+ PATUNFETCH;
+ break;
+ }
+ }
+
+ /* Now we know whether 0 matches is allowed,
+ and whether 2 or more matches is allowed. */
+ if (many_times_ok)
+ {
+ /* If more than one repetition is allowed,
+ put in a backward jump at the end. */
+ store_jump (b, maybe_finalize_jump, laststart - 3);
+ b += 3;
+ }
+ insert_jump (on_failure_jump, laststart, b + 3, b);
+ pending_exact = 0;
+ b += 3;
+ if (!zero_times_ok)
+ {
+ /* At least one repetition required: insert before the loop
+ a skip over the initial on-failure-jump instruction */
+ insert_jump (dummy_failure_jump, laststart, laststart + 6, b);
+ b += 3;
+ }
+ break;
+
+ case '.':
+ laststart = b;
+ PATPUSH (anychar);
+ break;
+
+ case '[':
+ if (b - bufp->buffer
+ > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH)
+ /* Note that EXTEND_BUFFER clobbers c */
+ EXTEND_BUFFER;
+
+ laststart = b;
+ if (*p == '^')
+ PATPUSH (charset_not), p++;
+ else
+ PATPUSH (charset);
+ p1 = p;
+
+ PATPUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+ /* Clear the whole map */
+ bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+ /* Read in characters and ranges, setting map bits */
+ while (1)
+ {
+ PATFETCH (c);
+ if (c == ']' && p != p1 + 1) break;
+ if (*p == '-')
+ {
+ PATFETCH (c1);
+ PATFETCH (c1);
+ while (c <= c1)
+ b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH), c++;
+ }
+ else
+ {
+ b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH);
+ }
+ }
+ /* Discard any bitmap bytes that are all 0 at the end of the map.
+ Decrement the map-length byte too. */
+ while (b[-1] > 0 && b[b[-1] - 1] == 0)
+ b[-1]--;
+ b += b[-1];
+ break;
+
+ case '\\':
+ if (p == pend) goto invalid_pattern;
+ PATFETCH_RAW (c);
+ switch (c)
+ {
+ case '(':
+ if (stackp == stacke) goto nesting_too_deep;
+ if (regnum < RE_NREGS)
+ {
+ PATPUSH (start_memory);
+ PATPUSH (regnum);
+ }
+ *stackp++ = b - bufp->buffer;
+ *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0;
+ *stackp++ = regnum++;
+ *stackp++ = begalt - bufp->buffer;
+ fixup_jump = 0;
+ laststart = 0;
+ begalt = b;
+ break;
+
+ case ')':
+ if (stackp == stackb) goto unmatched_close;
+ begalt = *--stackp + bufp->buffer;
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ if (stackp[-1] < RE_NREGS)
+ {
+ PATPUSH (stop_memory);
+ PATPUSH (stackp[-1]);
+ }
+ stackp -= 2;
+ fixup_jump = 0;
+ if (*stackp)
+ fixup_jump = *stackp + bufp->buffer - 1;
+ laststart = *--stackp + bufp->buffer;
+ break;
+
+ case '|':
+ insert_jump (on_failure_jump, begalt, b + 6, b);
+ pending_exact = 0;
+ b += 3;
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+ fixup_jump = b;
+ b += 3;
+ laststart = 0;
+ begalt = b;
+ break;
+
+#ifdef emacs
+ case '=':
+ PATPUSH (at_dot);
+ break;
+
+ case 's':
+ laststart = b;
+ PATPUSH (syntaxspec);
+ PATFETCH (c);
+ PATPUSH (syntax_spec_code[c]);
+ break;
+
+ case 'S':
+ laststart = b;
+ PATPUSH (notsyntaxspec);
+ PATFETCH (c);
+ PATPUSH (syntax_spec_code[c]);
+ break;
+#endif emacs
+
+ case 'w':
+ laststart = b;
+ PATPUSH (wordchar);
+ break;
+
+ case 'W':
+ laststart = b;
+ PATPUSH (notwordchar);
+ break;
+
+ case '<':
+ PATPUSH (wordbeg);
+ break;
+
+ case '>':
+ PATPUSH (wordend);
+ break;
+
+ case 'b':
+ PATPUSH (wordbound);
+ break;
+
+ case 'B':
+ PATPUSH (notwordbound);
+ break;
+
+ case '`':
+ PATPUSH (begbuf);
+ break;
+
+ case '\'':
+ PATPUSH (endbuf);
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ c1 = c - '0';
+ if (c1 >= regnum)
+ goto normal_char;
+ for (stackt = stackp - 2; stackt > stackb; stackt -= 4)
+ if (*stackt == c1)
+ goto normal_char;
+ laststart = b;
+ PATPUSH (duplicate);
+ PATPUSH (c1);
+ break;
+ default:
+ goto normal_char;
+ }
+ break;
+
+ default:
+ normal_char:
+ if (!pending_exact || pending_exact + *pending_exact + 1 != b
+ || *pending_exact == 0177 || *p == '*' || *p == '^'
+ || *p == '+' || *p == '?')
+ {
+ laststart = b;
+ PATPUSH (exactn);
+ pending_exact = b;
+ PATPUSH (0);
+ }
+ PATPUSH (c);
+ (*pending_exact)++;
+ }
+ }
+
+ if (fixup_jump)
+ store_jump (fixup_jump, jump, b);
+
+ if (stackp != stackb) goto unmatched_open;
+
+ bufp->used = b - bufp->buffer;
+ return 0;
+
+ invalid_pattern:
+ return "Invalid regular expression";
+
+ unmatched_open:
+ return "Unmatched \\(";
+
+ unmatched_close:
+ return "Unmatched \\)";
+
+ end_of_pattern:
+ return "Premature end of regular expression";
+
+ nesting_too_deep:
+ return "Nesting too deep";
+
+ too_big:
+ return "Regular expression too big";
+
+ memory_exhausted:
+ return "Memory exhausted";
+}
+
+/* Store where `from' points a jump operation to jump to where `to' points.
+ `opcode' is the opcode to store. */
+
+static int
+store_jump (from, opcode, to)
+ char *from, *to;
+ char opcode;
+{
+ from[0] = opcode;
+ from[1] = (to - (from + 3)) & 0377;
+ from[2] = (to - (from + 3)) >> 8;
+}
+
+/* Open up space at char FROM, and insert there a jump to TO.
+ CURRENT_END gives te end of the storage no in use,
+ so we know how much data to copy up.
+ OP is the opcode of the jump to insert.
+
+ If you call this function, you must zero out pending_exact. */
+
+static int
+insert_jump (op, from, to, current_end)
+ char op;
+ char *from, *to, *current_end;
+{
+ register char *pto = current_end + 3;
+ register char *pfrom = current_end;
+ while (pfrom != from)
+ *--pto = *--pfrom;
+ store_jump (from, op, to);
+}
+
+/* Given a pattern, compute a fastmap from it.
+ The fastmap records which of the (1 << BYTEWIDTH) possible characters
+ can start a string that matches the pattern.
+ This fastmap is used by re_search to skip quickly over totally implausible text.
+
+ The caller must supply the address of a (1 << BYTEWIDTH)-byte data area
+ as bufp->fastmap.
+ The other components of bufp describe the pattern to be used. */
+
+VOID
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ unsigned char *pattern = (unsigned char *) bufp->buffer;
+ int size = bufp->used;
+ register char *fastmap = bufp->fastmap;
+ register unsigned char *p = pattern;
+ register unsigned char *pend = pattern + size;
+ register int j, k;
+ unsigned char *translate = (unsigned char *) bufp->translate;
+
+ unsigned char *stackb[NFAILURES];
+ unsigned char **stackp = stackb;
+
+ bzero (fastmap, (1 << BYTEWIDTH));
+ bufp->fastmap_accurate = 1;
+ bufp->can_be_null = 0;
+
+ while (p)
+ {
+ if (p == pend)
+ {
+ bufp->can_be_null = 1;
+ break;
+ }
+ switch ((regexpcode) *p++)
+ {
+ case exactn:
+ if (translate)
+ fastmap[translate[p[1]]] = 1;
+ else
+ fastmap[p[1]] = 1;
+ break;
+
+ case begline:
+ case before_dot:
+ case at_dot:
+ case after_dot:
+ case begbuf:
+ case endbuf:
+ case wordbound:
+ case notwordbound:
+ case wordbeg:
+ case wordend:
+ continue;
+
+ case endline:
+ if (translate)
+ fastmap[translate['\n']] = 1;
+ else
+ fastmap['\n'] = 1;
+ bufp->can_be_null = 1;
+ break;
+
+ case finalize_jump:
+ case maybe_finalize_jump:
+ case jump:
+ case dummy_failure_jump:
+ bufp->can_be_null = 1;
+ j = *p++ & 0377;
+ j += SIGN_EXTEND_CHAR (*(char *)p++) << 8;
+ p += j;
+ if (j > 0)
+ continue;
+ /* Jump backward reached implies we just went through
+ the body of a loop and matched nothing.
+ Opcode jumped to should be an on_failure_jump.
+ Just treat it like an ordinary jump.
+ For a * loop, it has pushed its failure point already;
+ if so, discard that as redundant. */
+ if ((regexpcode) *p != on_failure_jump)
+ continue;
+ p++;
+ j = *p++ & 0377;
+ j += SIGN_EXTEND_CHAR (*(char *)p++) << 8;
+ p += j;
+ if (stackp != stackb && *stackp == p)
+ stackp--;
+ continue;
+
+ case on_failure_jump:
+ j = *p++ & 0377;
+ j += SIGN_EXTEND_CHAR (*(char *)p++) << 8;
+ *++stackp = p + j;
+ continue;
+
+ case start_memory:
+ case stop_memory:
+ p++;
+ continue;
+
+ case duplicate:
+ bufp->can_be_null = 1;
+ fastmap['\n'] = 1;
+ case anychar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (j != '\n')
+ fastmap[j] = 1;
+ if (bufp->can_be_null)
+ return;
+ /* Don't return; check the alternative paths
+ so we can set can_be_null if appropriate. */
+ break;
+
+ case wordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == Sword)
+ fastmap[j] = 1;
+ break;
+
+ case notwordchar:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != Sword)
+ fastmap[j] = 1;
+ break;
+
+#ifdef emacs
+ case syntaxspec:
+ k = *p++;
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) == (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+
+ case notsyntaxspec:
+ for (j = 0; j < (1 << BYTEWIDTH); j++)
+ if (SYNTAX (j) != (enum syntaxcode) k)
+ fastmap[j] = 1;
+ break;
+#endif emacs
+
+ case charset:
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+ {
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+ }
+ break;
+
+ case charset_not:
+ /* Chars beyond end of map must be allowed */
+ for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+
+ for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+ if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+ {
+ if (translate)
+ fastmap[translate[j]] = 1;
+ else
+ fastmap[j] = 1;
+ }
+ break;
+ }
+
+ /* Get here means we have successfully found the possible starting characters
+ of one path of the pattern. We need not follow this path any farther.
+ Instead, look at the next alternative remembered in the stack. */
+ if (stackp != stackb)
+ p = *stackp--;
+ else
+ break;
+ }
+}
+
+/* Like re_search_2, below, but only one string is specified. */
+
+int
+re_search (pbufp, string, size, startpos, range, regs)
+ struct re_pattern_buffer *pbufp;
+ char *string;
+ int size, startpos, range;
+ struct re_registers *regs;
+{
+ return re_search_2 (pbufp, 0, 0, string, size, startpos, range, regs, size);
+}
+
+/* Like re_match_2 but tries first a match starting at index `startpos',
+ then at startpos + 1, and so on.
+ `range' is the number of places to try before giving up.
+ If `range' is negative, the starting positions tried are
+ startpos, startpos - 1, etc.
+ It is up to the caller to make sure that range is not so large
+ as to take the starting position outside of the input strings.
+
+The value returned is the position at which the match was found,
+ or -1 if no match was found. */
+
+int
+re_search_2 (pbufp, string1, size1, string2, size2, startpos, range, regs, mstop)
+ struct re_pattern_buffer *pbufp;
+ char *string1, *string2;
+ int size1, size2;
+ int startpos;
+ register int range;
+ struct re_registers *regs;
+ int mstop;
+{
+ register char *fastmap = pbufp->fastmap;
+ register char *translate = pbufp->translate;
+ int total = size1 + size2;
+
+ /* Update the fastmap now if not correct already */
+ if (fastmap && !pbufp->fastmap_accurate)
+ re_compile_fastmap (pbufp);
+
+ while (1)
+ {
+ /* If a fastmap is supplied, skip quickly over characters
+ that cannot possibly be the start of a match.
+ Note, however, that if the pattern can possibly match
+ the null string, we must test it at each starting point
+ so that we take the first null string we get. */
+
+ if (fastmap && startpos < total && !pbufp->can_be_null)
+ {
+ if (range > 0)
+ {
+ register int lim = 0;
+ register char *p;
+ int irange = range;
+ if (startpos < size1 && startpos + range >= size1)
+ lim = range - (size1 - startpos);
+
+ p = &(startpos >= size1 ? string2 - size1 : string1)[startpos];
+
+ if (translate)
+ {
+ while (range > lim && !fastmap[translate[*p++]])
+ range--;
+ }
+ else
+ {
+ while (range > lim && !fastmap[*p++])
+ range--;
+ }
+ startpos += irange - range;
+ }
+ else
+ {
+ register char c;
+ if (startpos >= size1) c = string2[startpos - size1];
+ else c = string1[startpos];
+ if (translate ? !fastmap[translate[c]] : !fastmap[c])
+ goto advance;
+ }
+ }
+
+ if (range >= 0 && startpos == total
+ && fastmap && !pbufp->can_be_null)
+ return -1;
+
+ if (0 <= re_match_2 (pbufp, string1, size1, string2, size2, startpos, regs, mstop))
+ return startpos;
+
+#ifdef C_ALLOCA
+ alloca (0);
+#endif /* C_ALLOCA */
+
+ advance:
+ if (!range) break;
+ if (range > 0) range--, startpos++; else range++, startpos--;
+ }
+ return -1;
+}
+
+#ifndef emacs /* emacs never uses this */
+int
+re_match (pbufp, string, size, pos, regs)
+ struct re_pattern_buffer *pbufp;
+ char *string;
+ int size, pos;
+ struct re_registers *regs;
+{
+ return re_match_2 (pbufp, 0, 0, string, size, pos, regs, size);
+}
+#endif /* emacs */
+
+/* Match the pattern described by `pbufp'
+ against data which is the virtual concatenation of `string1' and `string2'.
+ `size1' and `size2' are the sizes of the two data strings.
+ Start the match at position `pos'.
+ Do not consider matching past the position `mstop'.
+
+ If pbufp->fastmap is nonzero, then it had better be up to date.
+
+ The reason that the data to match is specified as two components
+ which are to be regarded as concatenated
+ is so that this function can be used directly on the contents of an Emacs buffer.
+
+ -1 is returned if there is no match. Otherwise the value is the length
+ of the substring which was matched.
+*/
+
+int
+re_match_2 (pbufp, string1, size1, string2, size2, pos, regs, mstop)
+ struct re_pattern_buffer *pbufp;
+ char *string1, *string2;
+ int size1, size2;
+ int pos;
+ struct re_registers *regs;
+ int mstop;
+{
+ register char *p = pbufp->buffer;
+ register char *pend = p + pbufp->used;
+ /* End of first string */
+ char *end1;
+ /* End of second string */
+ char *end2;
+ /* Pointer just past last char to consider matching */
+ char *end_match_1, *end_match_2;
+ register char *d, *dend;
+ register int mcnt;
+ char *translate = pbufp->translate;
+
+ /* Failure point stack. Each place that can handle a failure further down the line
+ pushes a failure point on this stack. It consists of two char *'s.
+ The first one pushed is where to resume scanning the pattern;
+ the second pushed is where to resume scanning the strings.
+ If the latter is zero, the failure point is a "dummy".
+ If a failure happens and the innermost failure point is dormant,
+ it discards that failure point and tries the next one. */
+
+ char **stackb = (char **) alloca (2 * NFAILURES * sizeof (char *));
+ char **stackp = stackb, **stacke = &stackb[2 * NFAILURES];
+
+ /* Information on the "contents" of registers.
+ These are pointers into the input strings; they record
+ just what was matched (on this attempt) by some part of the pattern.
+ The start_memory command stores the start of a register's contents
+ and the stop_memory command stores the end.
+
+ At that point, regstart[regnum] points to the first character in the register,
+ regend[regnum] points to the first character beyond the end of the register,
+ and regstart_segend[regnum] is either the same as regend[regnum]
+ or else points to the end of the input string into which regstart[regnum] points.
+ The latter case happens when regstart[regnum] is in string1 and
+ regend[regnum] is in string2. */
+
+ char *regstart[RE_NREGS];
+ char *regstart_segend[RE_NREGS];
+ char *regend[RE_NREGS];
+
+ /* Set up pointers to ends of strings.
+ Don't allow the second string to be empty unless both are empty. */
+ if (!size2)
+ {
+ string2 = string1;
+ size2 = size1;
+ string1 = 0;
+ size1 = 0;
+ }
+ end1 = string1 + size1;
+ end2 = string2 + size2;
+
+ /* Compute where to stop matching, within the two strings */
+ if (mstop <= size1)
+ {
+ end_match_1 = string1 + mstop;
+ end_match_2 = string2;
+ }
+ else
+ {
+ end_match_1 = end1;
+ end_match_2 = string2 + mstop - size1;
+ }
+
+ /* Initialize \( and \) text positions to -1
+ to mark ones that no \( or \) has been seen for. */
+
+ for (mcnt = 0; mcnt < sizeof (regstart) / sizeof (*regstart); mcnt++)
+ regstart[mcnt] = (char *) -1;
+
+ /* `p' scans through the pattern as `d' scans through the data.
+ `dend' is the end of the input string that `d' points within.
+ `d' is advanced into the following input string whenever necessary,
+ but this happens before fetching;
+ therefore, at the beginning of the loop,
+ `d' can be pointing at the end of a string,
+ but it cannot equal string2. */
+
+ if (pos <= size1)
+ d = string1 + pos, dend = end_match_1;
+ else
+ d = string2 + pos - size1, dend = end_match_2;
+
+/* Write PREFETCH; just before fetching a character with *d. */
+#define PREFETCH \
+ while (d == dend) \
+ { if (dend == end_match_2) goto fail; /* end of string2 => failure */ \
+ d = string2; /* end of string1 => advance to string2. */ \
+ dend = end_match_2; }
+
+ /* This loop loops over pattern commands.
+ It exits by returning from the function if match is complete,
+ or it drops through if match fails at this starting point in the input data. */
+
+ while (1)
+ {
+ if (p == pend)
+ /* End of pattern means we have succeeded! */
+ {
+ /* If caller wants register contents data back, convert it to indices */
+ if (regs)
+ {
+ regend[0] = d;
+ regstart[0] = string1;
+ for (mcnt = 0; mcnt < RE_NREGS; mcnt++)
+ {
+ if ((mcnt != 0) && regstart[mcnt] == (char *) -1)
+ {
+ regs->start[mcnt] = -1;
+ regs->end[mcnt] = -1;
+ continue;
+ }
+ if (regstart[mcnt] - string1 < 0 ||
+ regstart[mcnt] - string1 > size1)
+ regs->start[mcnt] = regstart[mcnt] - string2 + size1;
+ else
+ regs->start[mcnt] = regstart[mcnt] - string1;
+ if (regend[mcnt] - string1 < 0 ||
+ regend[mcnt] - string1 > size1)
+ regs->end[mcnt] = regend[mcnt] - string2 + size1;
+ else
+ regs->end[mcnt] = regend[mcnt] - string1;
+ }
+ regs->start[0] = pos;
+ }
+ if (d - string1 >= 0 && d - string1 <= size1)
+ return d - string1 - pos;
+ else
+ return d - string2 + size1 - pos;
+ }
+
+ /* Otherwise match next pattern command */
+ switch ((regexpcode) *p++)
+ {
+
+ /* \( is represented by a start_memory, \) by a stop_memory.
+ Both of those commands contain a "register number" argument.
+ The text matched within the \( and \) is recorded under that number.
+ Then, \<digit> turns into a `duplicate' command which
+ is followed by the numeric value of <digit> as the register number. */
+
+ case start_memory:
+ regstart[*p] = d;
+ regstart_segend[*p++] = dend;
+ break;
+
+ case stop_memory:
+ regend[*p] = d;
+ if (regstart_segend[*p] == dend)
+ regstart_segend[*p] = d;
+ p++;
+ break;
+
+ case duplicate:
+ {
+ int regno = *p++; /* Get which register to match against */
+ register char *d2, *dend2;
+
+ d2 = regstart[regno];
+ dend2 = regstart_segend[regno];
+ while (1)
+ {
+ /* Advance to next segment in register contents, if necessary */
+ while (d2 == dend2)
+ {
+ if (dend2 == end_match_2) break;
+ if (dend2 == regend[regno]) break;
+ d2 = string2, dend2 = regend[regno]; /* end of string1 => advance to string2. */
+ }
+ /* At end of register contents => success */
+ if (d2 == dend2) break;
+
+ /* Advance to next segment in data being matched, if necessary */
+ PREFETCH;
+
+ /* mcnt gets # consecutive chars to compare */
+ mcnt = dend - d;
+ if (mcnt > dend2 - d2)
+ mcnt = dend2 - d2;
+ /* Compare that many; failure if mismatch, else skip them. */
+ if (translate ? bcmp_translate (d, d2, mcnt, translate) : bcmp (d, d2, mcnt))
+ goto fail;
+ d += mcnt, d2 += mcnt;
+ }
+ }
+ break;
+
+ case anychar:
+ /* fetch a data character */
+ PREFETCH;
+ /* Match anything but a newline. */
+ if ((translate ? translate[*d++] : *d++) == '\n')
+ goto fail;
+ break;
+
+ case charset:
+ case charset_not:
+ {
+ /* Nonzero for charset_not */
+ int not = 0;
+ register int c;
+ if (*(p - 1) == (char) charset_not)
+ not = 1;
+
+ /* fetch a data character */
+ PREFETCH;
+
+ if (translate)
+ c = translate [*(unsigned char *)d];
+ else
+ c = *(unsigned char *)d;
+
+ if (c < *p * BYTEWIDTH
+ && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+
+ p += 1 + *p;
+
+ if (!not) goto fail;
+ d++;
+ break;
+ }
+
+ case begline:
+ if (d == string1 || d[-1] == '\n')
+ break;
+ goto fail;
+
+ case endline:
+ if (d == end2
+ || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n'))
+ break;
+ goto fail;
+
+ /* "or" constructs ("|") are handled by starting each alternative
+ with an on_failure_jump that points to the start of the next alternative.
+ Each alternative except the last ends with a jump to the joining point.
+ (Actually, each jump except for the last one really jumps
+ to the following jump, because tensioning the jumps is a hassle.) */
+
+ /* The start of a stupid repeat has an on_failure_jump that points
+ past the end of the repeat text.
+ This makes a failure point so that, on failure to match a repetition,
+ matching restarts past as many repetitions have been found
+ with no way to fail and look for another one. */
+
+ /* A smart repeat is similar but loops back to the on_failure_jump
+ so that each repetition makes another failure point. */
+
+ case on_failure_jump:
+ if (stackp == stacke)
+ {
+ char **stackx = (char **) alloca (2 * (stacke - stackb) * sizeof (char *));
+ bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *));
+ stackp += stackx - stackb;
+ stacke = stackx + 2 * (stacke - stackb);
+ stackb = stackx;
+ }
+ mcnt = *p++ & 0377;
+ mcnt += SIGN_EXTEND_CHAR (*p++) << 8;
+ *stackp++ = mcnt + p;
+ *stackp++ = d;
+ break;
+
+ /* The end of a smart repeat has an maybe_finalize_jump back.
+ Change it either to a finalize_jump or an ordinary jump. */
+
+ case maybe_finalize_jump:
+ mcnt = *p++ & 0377;
+ mcnt += SIGN_EXTEND_CHAR (*p++) << 8;
+ /* Compare what follows with the begining of the repeat.
+ If we can establish that there is nothing that they would
+ both match, we can change to finalize_jump */
+ if (p == pend)
+ p[-3] = (char) finalize_jump;
+ else if (*p == (char) exactn || *p == (char) endline)
+ {
+ register int c = *p == (char) endline ? '\n' : p[2];
+ register char *p1 = p + mcnt;
+ /* p1[0] ... p1[2] are an on_failure_jump.
+ Examine what follows that */
+ if (p1[3] == (char) exactn && p1[5] != c)
+ p[-3] = (char) finalize_jump;
+ else if (p1[3] == (char) charset || p1[3] == (char) charset_not)
+ {
+ int not = p1[3] == (char) charset_not;
+ if (c < p1[4] * BYTEWIDTH
+ && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+ not = !not;
+ /* not is 1 if c would match */
+ /* That means it is not safe to finalize */
+ if (!not)
+ p[-3] = (char) finalize_jump;
+ }
+ }
+ p -= 2;
+ if (p[-1] != (char) finalize_jump)
+ {
+ p[-1] = (char) jump;
+ goto nofinalize;
+ }
+
+ /* The end of a stupid repeat has a finalize-jump
+ back to the start, where another failure point will be made
+ which will point after all the repetitions found so far. */
+
+ case finalize_jump:
+ stackp -= 2;
+
+ case jump:
+ nofinalize:
+ mcnt = *p++ & 0377;
+ mcnt += SIGN_EXTEND_CHAR (*p++) << 8;
+ p += mcnt;
+ break;
+
+ case dummy_failure_jump:
+ if (stackp == stacke)
+ {
+ char **stackx = (char **) alloca (2 * (stacke - stackb) * sizeof (char *));
+ bcopy (stackb, stackx, (stacke - stackb) * sizeof (char *));
+ stackp += stackx - stackb;
+ stacke = stackx + 2 * (stacke - stackb);
+ stackb = stackx;
+ }
+ *stackp++ = 0;
+ *stackp++ = 0;
+ goto nofinalize;
+
+ case wordbound:
+ if (d == string1 /* Points to first char */
+ || d == end2 /* Points to end */
+ || (d == end1 && size2 == 0)) /* Points to end */
+ break;
+ if ((SYNTAX (((unsigned char *)d)[-1]) == Sword)
+ != (SYNTAX (d == end1 ? *(unsigned char *)string2 : *(unsigned char *)d) == Sword))
+ break;
+ goto fail;
+
+ case notwordbound:
+ if (d == string1 /* Points to first char */
+ || d == end2 /* Points to end */
+ || (d == end1 && size2 == 0)) /* Points to end */
+ goto fail;
+ if ((SYNTAX (((unsigned char *)d)[-1]) == Sword)
+ != (SYNTAX (d == end1 ? *(unsigned char *)string2 : *(unsigned char *)d) == Sword))
+ goto fail;
+ break;
+
+ case wordbeg:
+ if (d == end2 /* Points to end */
+ || (d == end1 && size2 == 0) /* Points to end */
+ || SYNTAX (*(unsigned char *) (d == end1 ? string2 : d)) != Sword) /* Next char not a letter */
+ goto fail;
+ if (d == string1 /* Points to first char */
+ || SYNTAX (((unsigned char *)d)[-1]) != Sword) /* prev char not letter */
+ break;
+ goto fail;
+
+ case wordend:
+ if (d == string1 /* Points to first char */
+ || SYNTAX (((unsigned char *)d)[-1]) != Sword) /* prev char not letter */
+ goto fail;
+ if (d == end2 /* Points to end */
+ || (d == end1 && size2 == 0) /* Points to end */
+ || SYNTAX (d == end1 ? *(unsigned char *)string2 : *(unsigned char *)d) != Sword) /* Next char not a letter */
+ break;
+ goto fail;
+
+#ifdef emacs
+ case before_dot:
+ if (((d - string2 <= (unsigned) size2)
+ ? d - (char *) bf_p2 : d - (char *) bf_p1)
+ <= point)
+ goto fail;
+ break;
+
+ case at_dot:
+ if (((d - string2 <= (unsigned) size2)
+ ? d - (char *) bf_p2 : d - (char *) bf_p1)
+ == point)
+ goto fail;
+ break;
+
+ case after_dot:
+ if (((d - string2 <= (unsigned) size2)
+ ? d - (char *) bf_p2 : d - (char *) bf_p1)
+ >= point)
+ goto fail;
+ break;
+
+ case wordchar:
+ mcnt = (int) Sword;
+ goto matchsyntax;
+
+ case syntaxspec:
+ mcnt = *p++;
+ matchsyntax:
+ PREFETCH;
+ if (SYNTAX (*(unsigned char *)d++) != (enum syntaxcode) mcnt) goto fail;
+ break;
+
+ case notwordchar:
+ mcnt = (int) Sword;
+ goto matchnotsyntax;
+
+ case notsyntaxspec:
+ mcnt = *p++;
+ matchnotsyntax:
+ PREFETCH;
+ if (SYNTAX (*(unsigned char *)d++) == (enum syntaxcode) mcnt) goto fail;
+ break;
+#else
+ case wordchar:
+ PREFETCH;
+ if (SYNTAX (*(unsigned char *)d++) == 0) goto fail;
+ break;
+
+ case notwordchar:
+ PREFETCH;
+ if (SYNTAX (*(unsigned char *)d++) != 0) goto fail;
+ break;
+#endif not emacs
+
+ case begbuf:
+ if (d == string1) /* Note, d cannot equal string2 */
+ break; /* unless string1 == string2. */
+ goto fail;
+
+ case endbuf:
+ if (d == end2 || (d == end1 && size2 == 0))
+ break;
+ goto fail;
+
+ case exactn:
+ /* Match the next few pattern characters exactly.
+ mcnt is how many characters to match. */
+ mcnt = *p++;
+ if (translate)
+ {
+ do
+ {
+ PREFETCH;
+ if (translate[*(unsigned char *)d++] != *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ else
+ {
+ do
+ {
+ PREFETCH;
+ if (*d++ != *p++) goto fail;
+ }
+ while (--mcnt);
+ }
+ break;
+ }
+ continue; /* Successfully matched one pattern command; keep matching */
+
+ /* Jump here if any matching operation fails. */
+ fail:
+ if (stackp != stackb)
+ /* A restart point is known. Restart there and pop it. */
+ {
+ if (!stackp[-2])
+ { /* If innermost failure point is dormant, flush it and keep looking */
+ stackp -= 2;
+ goto fail;
+ }
+ d = *--stackp;
+ p = *--stackp;
+ if (d >= string1 && d <= end1)
+ dend = end_match_1;
+ }
+ else break; /* Matching at this starting point really fails! */
+ }
+ return -1; /* Failure to match */
+}
+
+static int
+bcmp_translate (s1, s2, len, translate)
+ char *s1, *s2;
+ register int len;
+ char *translate;
+{
+ register char *p1 = s1, *p2 = s2;
+ while (len)
+ {
+ if (translate [*p1++] != translate [*p2++]) return 1;
+ len--;
+ }
+ return 0;
+}
+
+/* Entry points compatible with bsd4.2 regex library */
+
+#ifndef emacs
+
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+ char *s;
+{
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return "No previous regular expression";
+ return 0;
+ }
+
+ if (!re_comp_buf.buffer)
+ {
+ if (!(re_comp_buf.buffer = (char *) malloc (200)))
+ return "Memory exhausted";
+ re_comp_buf.allocated = 200;
+ if (!(re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH)))
+ return "Memory exhausted";
+ }
+ return re_compile_pattern (s, strlen (s), &re_comp_buf);
+}
+
+int
+re_exec (s)
+ char *s;
+{
+ int len = strlen (s);
+ return 0 <= re_search (&re_comp_buf, s, len, 0, len, 0);
+}
+
+#endif /* emacs */
+
+#ifdef test
+
+#include <stdio.h>
+
+/* Indexed by a character, gives the upper case equivalent of the character */
+
+static char upcase[0400] =
+ { 000, 001, 002, 003, 004, 005, 006, 007,
+ 010, 011, 012, 013, 014, 015, 016, 017,
+ 020, 021, 022, 023, 024, 025, 026, 027,
+ 030, 031, 032, 033, 034, 035, 036, 037,
+ 040, 041, 042, 043, 044, 045, 046, 047,
+ 050, 051, 052, 053, 054, 055, 056, 057,
+ 060, 061, 062, 063, 064, 065, 066, 067,
+ 070, 071, 072, 073, 074, 075, 076, 077,
+ 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
+ 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
+ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
+ 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
+ };
+
+main ()
+{
+ char pat[80];
+ struct re_pattern_buffer buf;
+ struct re_registers regs;
+ int i;
+ char c;
+ char fastmap[(1 << BYTEWIDTH)];
+
+ buf.allocated = 40;
+ buf.buffer = (char *) malloc (buf.allocated);
+ buf.fastmap = fastmap;
+ buf.translate = upcase;
+
+ while (1)
+ {
+ printf("Enter pattern\n");
+ gets (pat);
+
+ if (*pat)
+ {
+ re_compile_pattern (pat, strlen(pat), &buf);
+
+ for (i = 0; i < buf.used; i++)
+ printchar (buf.buffer[i]);
+
+ putchar ('\n');
+
+ printf ("%d allocated, %d used.\n", buf.allocated, buf.used);
+
+ re_compile_fastmap (&buf);
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (fastmap[i]) printchar (i);
+ putchar ('\n');
+ }
+
+ printf("enter string to search\n");
+ gets (pat); /* Now read the string to match against */
+
+/* i = re_match (&buf, pat, strlen (pat), 0, 0); */
+ i = re_search (&buf, pat, strlen (pat), 0, strlen (pat), &regs);
+ printf ("Match value %d.\n", i);
+ for (i=0; i < RE_NREGS; i++)
+ printf("%2d start %2d end %2d\n", i, regs.start[i], regs.end[i]);
+ }
+}
+
+#ifdef NOTDEF
+print_buf (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ int i;
+
+ printf ("buf is :\n----------------\n");
+ for (i = 0; i < bufp->used; i++)
+ printchar (bufp->buffer[i]);
+
+ printf ("\n%d allocated, %d used.\n", bufp->allocated, bufp->used);
+
+ printf ("Allowed by fastmap: ");
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->fastmap[i])
+ printchar (i);
+ printf ("\nAllowed by translate: ");
+ if (bufp->translate)
+ for (i = 0; i < (1 << BYTEWIDTH); i++)
+ if (bufp->translate[i])
+ printchar (i);
+ printf ("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't");
+ printf ("can %s be null\n----------", bufp->can_be_null ? "" : "not");
+}
+#endif
+
+printchar (c)
+ char c;
+{
+ if (c < 041 || c >= 0177)
+ {
+ putchar ('\\');
+ putchar (((c >> 6) & 3) + '0');
+ putchar (((c >> 3) & 7) + '0');
+ putchar ((c & 7) + '0');
+ }
+ else
+ putchar (c);
+}
+
+error (string)
+ char *string;
+{
+ puts (string);
+ exit (1);
+}
+
+#endif /* test */
+#endif /* REGEX */
diff --git a/usr.bin/mg/regex.h b/usr.bin/mg/regex.h
new file mode 100644
index 00000000000..c0a37b7a9b4
--- /dev/null
+++ b/usr.bin/mg/regex.h
@@ -0,0 +1,106 @@
+/* Definitions for data structures callers pass the regex library.
+ Copyright (C) 1985 Richard M. Stallman
+
+This program is distributed in the hope that it will be useful,
+but without any warranty. No author or distributor
+accepts responsibility to anyone for the consequences of using it
+or for whether it serves any particular purpose or works at all,
+unless he says so in writing.
+
+ Permission is granted to anyone to distribute verbatim copies
+ of this program's source code as received, in any medium, provided that
+ the copyright notice, the nonwarraty notice above
+ and this permission notice are preserved,
+ and that the distributor grants the recipient all rights
+ for further redistribution as permitted by this notice,
+ and informs him of these rights.
+
+ Permission is granted to distribute modified versions of this
+ program's source code, or of portions of it, under the above
+ conditions, plus the conditions that all changed files carry
+ prominent notices stating who last changed them and that the
+ derived material, including anything packaged together with it and
+ conceptually functioning as a modification of it rather than an
+ application of it, is in its entirety subject to a permission
+ notice identical to this one.
+
+ Permission is granted to distribute this program (verbatim or
+ as modified) in compiled or executable form, provided verbatim
+ redistribution is permitted as stated above for source code, and
+ A. it is accompanied by the corresponding machine-readable
+ source code, under the above conditions, or
+ B. it is accompanied by a written offer, with no time limit,
+ to distribute the corresponding machine-readable source code,
+ under the above conditions, to any one, in return for reimbursement
+ of the cost of distribution. Verbatim redistribution of the
+ written offer must be permitted. Or,
+ C. it is distributed by someone who received only the
+ compiled or executable form, and is accompanied by a copy of the
+ written offer of source code which he received along with it.
+
+ Permission is granted to distribute this program (verbatim or as modified)
+ in executable form as part of a larger system provided that the source
+ code for this program, including any modifications used,
+ is also distributed or offered as stated in the preceding paragraph.
+
+In other words, you are welcome to use, share and improve this program.
+You are forbidden to forbid anyone else to use, share and improve
+what you give them. Help stamp out software-hoarding! */
+
+
+#ifndef RE_NREGS
+#define RE_NREGS 10
+#endif
+
+/* This data structure is used to represent a compiled pattern. */
+
+struct re_pattern_buffer
+ {
+ char *buffer; /* Space holding the compiled pattern commands. */
+ int allocated; /* Size of space that buffer points to */
+ int used; /* Length of portion of buffer actually occupied */
+ char *fastmap; /* Pointer to fastmap, if any, or zero if none. */
+ /* re_search uses the fastmap, if there is one,
+ to skip quickly over totally implausible characters */
+ char *translate; /* Translate table to apply to all characters before comparing.
+ Or zero for no translation.
+ The translation is applied to a pattern when it is compiled
+ and to data when it is matched. */
+ char fastmap_accurate;
+ /* Set to zero when a new pattern is stored,
+ set to one when the fastmap is updated from it. */
+ char can_be_null; /* Set to one by compiling fastmap
+ if this pattern might match the null string.
+ It does not necessarily match the null string
+ in that case, but if this is zero, it cannot. */
+ };
+
+/* Structure to store "register" contents data in.
+
+ Pass the address of such a structure as an argument to re_match, etc.,
+ if you want this information back.
+
+ start[i] and end[i] record the string matched by \( ... \) grouping i,
+ for i from 1 to RE_NREGS - 1.
+ start[0] and end[0] record the entire string matched. */
+
+struct re_registers
+ {
+ int start[RE_NREGS];
+ int end[RE_NREGS];
+ };
+
+
+extern char *re_compile_pattern ();
+/* Is this really advertised? */
+extern VOID re_compile_fastmap ();
+extern int re_search (), re_search_2 ();
+extern int re_match (), re_match_2 ();
+
+/* 4.2 bsd compatibility (yuck) */
+extern char *re_comp ();
+extern int re_exec ();
+
+#ifdef SYNTAX_TABLE
+extern char *re_syntax_table;
+#endif
diff --git a/usr.bin/mg/region.c b/usr.bin/mg/region.c
new file mode 100644
index 00000000000..8112296f81a
--- /dev/null
+++ b/usr.bin/mg/region.c
@@ -0,0 +1,291 @@
+/*
+ * 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 "def.h"
+
+/*
+ * Kill the region. Ask "getregion"
+ * to figure out the bounds of the region.
+ * Move "." to the start, and kill the characters.
+ */
+/*ARGSUSED*/
+killregion(f, n)
+{
+ register int s;
+ REGION region;
+
+ if ((s=getregion(&region)) != TRUE)
+ return (s);
+ if ((lastflag&CFKILL) == 0) /* This is a kill type */
+ kdelete(); /* command, so do magic */
+ thisflag |= CFKILL; /* kill buffer stuff. */
+ curwp->w_dotp = region.r_linep;
+ curwp->w_doto = region.r_offset;
+ return (ldelete(region.r_size, KFORW));
+}
+
+/*
+ * Copy all of the characters in the
+ * region to the kill buffer. Don't move dot
+ * at all. This is a bit like a kill region followed
+ * by a yank.
+ */
+/*ARGSUSED*/
+copyregion(f, n)
+{
+ register LINE *linep;
+ register int loffs;
+ register int s;
+ REGION region;
+ VOID kdelete();
+
+ if ((s=getregion(&region)) != TRUE)
+ return s;
+ if ((lastflag&CFKILL) == 0) /* Kill type command. */
+ kdelete();
+ thisflag |= CFKILL;
+ linep = region.r_linep; /* Current line. */
+ loffs = region.r_offset; /* Current offset. */
+ while (region.r_size--) {
+ if (loffs == llength(linep)) { /* End of line. */
+ if ((s=kinsert('\n', KFORW)) != TRUE)
+ return (s);
+ linep = lforw(linep);
+ loffs = 0;
+ } else { /* Middle of line. */
+ if ((s=kinsert(lgetc(linep, loffs), KFORW)) != TRUE)
+ return s;
+ ++loffs;
+ }
+ }
+ 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*/
+lowerregion(f, n)
+{
+ register LINE *linep;
+ register int loffs;
+ register int c;
+ register int s;
+ REGION region;
+
+ if ((s=getregion(&region)) != TRUE)
+ return s;
+ lchange(WFHARD);
+ 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*/
+upperregion(f, n)
+{
+ register LINE *linep;
+ register int loffs;
+ register int c;
+ register int s;
+ REGION region;
+ VOID lchange();
+
+ if ((s=getregion(&region)) != TRUE)
+ return s;
+ lchange(WFHARD);
+ 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 clobberring" flag.
+ */
+getregion(rp) register REGION *rp; {
+ register LINE *flp;
+ register LINE *blp;
+ register long fsize; /* Long now. */
+ register long bsize;
+
+ if (curwp->w_markp == NULL) {
+ ewprintf("No mark set in this window");
+ return (FALSE);
+ }
+ if (curwp->w_dotp == curwp->w_markp) { /* "r_size" always ok. */
+ rp->r_linep = curwp->w_dotp;
+ 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;
+ }
+ flp = blp = curwp->w_dotp; /* Get region size. */
+ bsize = curwp->w_doto;
+ fsize = llength(flp)-curwp->w_doto+1;
+ while (lforw(flp)!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
+ if (lforw(flp) != curbp->b_linep) {
+ flp = lforw(flp);
+ if (flp == curwp->w_markp) {
+ rp->r_linep = curwp->w_dotp;
+ rp->r_offset = curwp->w_doto;
+ return (setsize(rp,
+ (RSIZE) (fsize+curwp->w_marko)));
+ }
+ fsize += llength(flp)+1;
+ }
+ if (lback(blp) != curbp->b_linep) {
+ blp = lback(blp);
+ bsize += llength(blp)+1;
+ if (blp == curwp->w_markp) {
+ rp->r_linep = blp;
+ rp->r_offset = curwp->w_marko;
+ return (setsize(rp,
+ (RSIZE) (bsize-curwp->w_marko)));
+ }
+ }
+ }
+ ewprintf("Bug: lost mark"); /* Gak! */
+ return FALSE;
+}
+
+/*
+ * Set size, and check for overflow.
+ */
+setsize(rp, size) register REGION *rp; register RSIZE size; {
+
+ rp->r_size = size;
+ if (rp->r_size != size) {
+ ewprintf("Region is too large");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#ifdef PREFIXREGION
+/*
+ * Implements one of my favorite keyboard macros; put a string at the
+ * beginning of a number of lines in a buffer. The quote string is
+ * settable by using set-prefix-string. Great for quoting mail, which
+ * is the real reason I wrote it, but also has uses for creating bar
+ * comments (like the one you're reading) in C code.
+ */
+
+#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*/
+prefixregion(f, n)
+{
+ register int s;
+ register LINE *first, *last;
+ register int nline;
+ REGION region;
+ char *prefix = prefix_string;
+
+ 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;
+
+ /* 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 prefix string.
+ */
+
+/*ARGSUSED*/
+setprefix(f, n)
+{
+ char buf[PREFIXLENGTH];
+ register int s;
+
+ if (prefix_string[0] == '\0')
+ s = ereply("Prefix string: ",buf,sizeof buf);
+ else
+ s = ereply("Prefix string (default %s): ",
+ buf,sizeof buf,prefix_string);
+ if (s == TRUE)
+ (VOID) strcpy(prefix_string, buf);
+ if ((s == FALSE) && (prefix_string[0] != '\0')) /* CR -- use old one */
+ s = TRUE;
+ return s;
+}
+#endif
diff --git a/usr.bin/mg/search.c b/usr.bin/mg/search.c
new file mode 100644
index 00000000000..3d127ce1f02
--- /dev/null
+++ b/usr.bin/mg/search.c
@@ -0,0 +1,664 @@
+/*
+ * 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 "def.h"
+#ifndef NO_MACRO
+#include "macro.h"
+#endif
+
+#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)
+
+typedef struct {
+ int s_code;
+ LINE *s_dotp;
+ int s_doto;
+} SRCHCOM;
+
+static SRCHCOM cmds[NSRCH];
+static int cip;
+
+int srch_lastdir = SRCH_NOPR; /* Last search flags. */
+
+static VOID is_cpush();
+static VOID is_lpush();
+static VOID is_pop();
+static int is_peek();
+static VOID is_undo();
+static int is_find();
+static VOID is_prompt();
+static VOID is_dspl();
+static int eq();
+
+/*
+ * 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*/
+forwsearch(f, n)
+{
+ register int s;
+
+ if ((s=readpattern("Search")) != TRUE)
+ return s;
+ if (forwsrch() == FALSE) {
+ 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*/
+backsearch(f, n)
+{
+ register int s;
+
+ if ((s=readpattern("Search backward")) != TRUE)
+ return (s);
+ if (backsrch() == FALSE) {
+ 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*/
+searchagain(f, n)
+{
+ if (srch_lastdir == SRCH_FORW) {
+ if (forwsrch() == FALSE) {
+ ewprintf("Search failed: \"%s\"", pat);
+ return FALSE;
+ }
+ return TRUE;
+ }
+ if (srch_lastdir == SRCH_BACK) {
+ if (backsrch() == FALSE) {
+ ewprintf("Search failed: \"%s\"", pat);
+ return FALSE;
+ }
+ return TRUE;
+ }
+ ewprintf("No last search");
+ return FALSE;
+}
+
+/*
+ * Use incremental searching, initially in the forward direction.
+ * isearch ignores any explicit arguments.
+ */
+/*ARGSUSED*/
+forwisearch(f, n)
+{
+ return isearch(SRCH_FORW);
+}
+
+/*
+ * Use incremental searching, initially in the reverse direction.
+ * isearch ignores any explicit arguments.
+ */
+/*ARGSUSED*/
+backisearch(f, n)
+{
+ return isearch(SRCH_BACK);
+}
+
+/*
+ * Incremental Search.
+ * dir is used as the initial direction to search.
+ * ^S switch direction to forward
+ * ^R switch direction to reverse
+ * ^Q quote next character (allows searching for ^N etc.)
+ * <ESC> exit from Isearch
+ * <DEL> undoes last character typed. (tricky job to do this correctly).
+ * other ^ exit search, don't set mark
+ * else accumulate into search string
+ */
+isearch(dir) {
+ register int c;
+ register LINE *clp;
+ register int cbo;
+ register int success;
+ int pptr;
+ char opat[NPAT];
+ VOID ungetkey();
+
+#ifndef NO_MACRO
+ if(macrodef) {
+ ewprintf("Can't isearch in macro");
+ return FALSE;
+ }
+#endif
+ for (cip=0; cip<NSRCH; cip++)
+ cmds[cip].s_code = SRCH_NOPR;
+ (VOID) strcpy(opat, pat);
+ cip = 0;
+ pptr = -1;
+ clp = curwp->w_dotp;
+ cbo = curwp->w_doto;
+ is_lpush();
+ is_cpush(SRCH_BEGIN);
+ success = TRUE;
+ is_prompt(dir, TRUE, success);
+ for (;;) {
+ update();
+ switch (c = getkey(FALSE)) {
+ case CCHR('['):
+ srch_lastdir = dir;
+ curwp->w_markp = clp;
+ curwp->w_marko = cbo;
+ 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_flag |= WFMOVE;
+ srch_lastdir = dir;
+ (VOID) ctrlg(FFRAND, 0);
+ (VOID) strcpy(pat, opat);
+ return ABORT;
+
+ case CCHR(']'):
+ case CCHR('S'):
+ if (dir == SRCH_BACK) {
+ dir = SRCH_FORW;
+ is_lpush();
+ is_cpush(SRCH_FORW);
+ success = TRUE;
+ }
+ if (success==FALSE && dir==SRCH_FORW)
+ break;
+ is_lpush();
+ pptr = strlen(pat);
+ (VOID) forwchar(FFRAND, 1);
+ if (is_find(SRCH_FORW) != FALSE) is_cpush(SRCH_MARK);
+ else {
+ (VOID) backchar(FFRAND, 1);
+ ttbeep();
+ success = FALSE;
+ }
+ 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)
+ break;
+ is_lpush();
+ pptr = strlen(pat);
+ (VOID) backchar(FFRAND, 1);
+ if (is_find(SRCH_BACK) != FALSE) is_cpush(SRCH_MARK);
+ else {
+ (VOID) forwchar(FFRAND, 1);
+ ttbeep();
+ success = FALSE;
+ }
+ 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;
+ case CCHR('M'):
+ c = CCHR('J');
+ goto addchar;
+
+ default:
+ if (ISCTRL(c)) {
+ ungetkey(c);
+ curwp->w_markp = clp;
+ curwp->w_marko = cbo;
+ ewprintf("Mark set");
+ curwp->w_flag |= WFMOVE;
+ return TRUE;
+ } /* and continue */
+ case CCHR('I'):
+ case CCHR('J'):
+ addchar:
+ if (pptr == -1)
+ pptr = 0;
+ if (pptr == 0)
+ success = TRUE;
+ pat[pptr++] = c;
+ if (pptr == NPAT) {
+ ewprintf("Pattern too long");
+ return FALSE;
+ }
+ pat[pptr] = '\0';
+ is_lpush();
+ if (success != FALSE) {
+ if (is_find(dir) != FALSE)
+ is_cpush(c);
+ else {
+ success = FALSE;
+ ttbeep();
+ is_cpush(SRCH_ACCM);
+ }
+ } else
+ is_cpush(SRCH_ACCM);
+ is_prompt(dir, FALSE, success);
+ }
+ }
+ /*NOTREACHED*/
+}
+
+static VOID
+is_cpush(cmd) register int cmd; {
+ if (++cip >= NSRCH)
+ cip = 0;
+ cmds[cip].s_code = cmd;
+}
+
+static VOID
+is_lpush() {
+ register 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;
+}
+
+static VOID
+is_pop() {
+ if (cmds[cip].s_code != SRCH_NOPR) {
+ curwp->w_doto = cmds[cip].s_doto;
+ curwp->w_dotp = cmds[cip].s_dotp;
+ curwp->w_flag |= WFMOVE;
+ cmds[cip].s_code = SRCH_NOPR;
+ }
+ if (--cip <= 0)
+ cip = NSRCH-1;
+}
+
+static int
+is_peek() {
+ return cmds[cip].s_code;
+}
+
+/* this used to always return TRUE (the return value was checked) */
+static VOID
+is_undo(pptr, dir) register int *pptr; register int *dir; {
+ register int redo = FALSE ;
+ switch (cmds[cip].s_code) {
+ case SRCH_BEGIN:
+ case SRCH_NOPR:
+ *pptr = -1;
+ 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(dir) register int dir; {
+ register int plen, odoto;
+ register LINE *odotp ;
+
+ odoto = curwp->w_doto;
+ odotp = curwp->w_dotp;
+ 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;
+ return FALSE;
+ }
+ return TRUE;
+ }
+ if (dir==SRCH_BACK) {
+ (VOID) forwchar(FFARG | FFRAND, plen);
+ if (backsrch() == FALSE) {
+ curwp->w_doto = odoto;
+ curwp->w_dotp = odotp;
+ return FALSE;
+ }
+ return TRUE;
+ }
+ 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(dir, flag, 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 "prompt" is just a string. The "flag" determines
+ * whether pat should be printed.
+ */
+static VOID
+is_dspl(prompt, flag) char *prompt; {
+
+ if (flag != FALSE)
+ ewprintf("%s: ", prompt);
+ else
+ ewprintf("%s: %s", prompt, pat);
+}
+
+/*
+ * Query Replace.
+ * Replace strings selectively. Does a search and replace operation.
+ */
+/*ARGSUSED*/
+queryrepl(f, n)
+{
+ register int s;
+ register int rcnt = 0; /* Replacements made so far */
+ register int plen; /* length of found string */
+ char news[NPAT]; /* replacement string */
+
+#ifndef NO_MACRO
+ if(macrodef) {
+ ewprintf("Can't query replace in macro");
+ return FALSE;
+ }
+#endif
+ if ((s=readpattern("Query replace")) != TRUE)
+ return (s);
+ if ((s=ereply("Query replace %s with: ",news, NPAT, pat)) == ABORT)
+ return (s);
+ if (s == FALSE)
+ 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();
+ switch (getkey(FALSE)) {
+ case ' ':
+ if (lreplace((RSIZE) plen, news, f) == FALSE)
+ return (FALSE);
+ rcnt++;
+ break;
+
+ case '.':
+ if (lreplace((RSIZE) plen, news, f) == FALSE)
+ return (FALSE);
+ rcnt++;
+ goto stopsearch;
+
+ case CCHR('G'): /* ^G or ESC */
+ (VOID) ctrlg(FFRAND, 0);
+ case CCHR('['):
+ goto stopsearch;
+
+ case '!':
+ do {
+ if (lreplace((RSIZE) plen, news, f) == FALSE)
+ return (FALSE);
+ rcnt++;
+ } while (forwsrch() == TRUE);
+ goto stopsearch;
+
+ case CCHR('H'):
+ case CCHR('?'): /* To not replace */
+ break;
+
+ default:
+ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
+ goto retry;
+ }
+ }
+stopsearch:
+ curwp->w_flag |= WFHARD;
+ update();
+ if (rcnt == 0)
+ ewprintf("(No replacements done)");
+ else if (rcnt == 1)
+ ewprintf("(1 replacement done)");
+ else
+ ewprintf("(%d replacements done)", 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.
+ */
+forwsrch() {
+ register LINE *clp;
+ register int cbo;
+ register LINE *tlp;
+ register int tbo;
+ char *pp;
+ register int c;
+
+ clp = curwp->w_dotp;
+ cbo = curwp->w_doto;
+ for(;;) {
+ if (cbo == llength(clp)) {
+ if((clp = lforw(clp)) == curbp->b_linep) break;
+ cbo = 0;
+ c = CCHR('J');
+ } else
+ c = lgetc(clp, cbo++);
+ if (eq(c, pat[0]) != FALSE) {
+ tlp = clp;
+ tbo = cbo;
+ pp = &pat[1];
+ while (*pp != 0) {
+ if (tbo == llength(tlp)) {
+ tlp = lforw(tlp);
+ if (tlp == curbp->b_linep)
+ goto fail;
+ tbo = 0;
+ c = CCHR('J');
+ } else
+ c = lgetc(tlp, tbo++);
+ if (eq(c, *pp++) == FALSE)
+ goto fail;
+ }
+ curwp->w_dotp = tlp;
+ curwp->w_doto = tbo;
+ curwp->w_flag |= 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.
+ */
+backsrch() {
+ register LINE *clp;
+ register int cbo;
+ register LINE *tlp;
+ register int tbo;
+ register int c;
+ register char *epp;
+ register char *pp;
+
+ for (epp = &pat[0]; epp[1] != 0; ++epp)
+ ;
+ clp = curwp->w_dotp;
+ cbo = curwp->w_doto;
+ for (;;) {
+ if (cbo == 0) {
+ clp = lback(clp);
+ if (clp == curbp->b_linep)
+ return FALSE;
+ cbo = llength(clp)+1;
+ }
+ if (--cbo == llength(clp))
+ c = CCHR('J');
+ else
+ c = lgetc(clp,cbo);
+ if (eq(c, *epp) != FALSE) {
+ tlp = clp;
+ tbo = cbo;
+ pp = epp;
+ while (pp != &pat[0]) {
+ if (tbo == 0) {
+ tlp = lback(tlp);
+ if (tlp == curbp->b_linep)
+ goto fail;
+ tbo = llength(tlp)+1;
+ }
+ if (--tbo == llength(tlp))
+ c = CCHR('J');
+ else
+ c = lgetc(tlp,tbo);
+ if (eq(c, *--pp) == FALSE)
+ goto fail;
+ }
+ curwp->w_dotp = tlp;
+ curwp->w_doto = tbo;
+ curwp->w_flag |= 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(bc, pc)
+register int bc, pc;
+{
+ bc = CHARMASK(bc);
+ pc = CHARMASK(pc);
+ if (bc == pc) return TRUE;
+ 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.
+ */
+readpattern(prompt) char *prompt; {
+ register int s;
+ char tpat[NPAT];
+
+ if (tpat[0] == '\0') s = ereply("%s: ", tpat, NPAT, prompt);
+ else s = ereply("%s: (default %s) ", tpat, NPAT, prompt, pat);
+
+ if (s == TRUE) /* Specified */
+ (VOID) strcpy(pat, tpat);
+ else if (s==FALSE && pat[0]!=0) /* CR, but old one */
+ s = TRUE;
+ return s;
+}
diff --git a/usr.bin/mg/spawn.c b/usr.bin/mg/spawn.c
new file mode 100644
index 00000000000..3d3f562b646
--- /dev/null
+++ b/usr.bin/mg/spawn.c
@@ -0,0 +1,120 @@
+/*
+ * Name: MicroGnuEmacs
+ * Spawn CLI for System V.
+ *
+ * Spawn for System V.
+ */
+#include "def.h"
+
+#include <signal.h>
+
+char *shellp = NULL; /* Saved "SHELL" program. */
+char *shname = NULL; /* Saved shell name */
+
+extern char *getenv();
+
+/*
+ * On System V, we no gots job control, so always run
+ * a subshell using fork/exec. Bound to "C-C", and used
+ * as a subcommand by "C-Z". (daveb)
+ *
+ * Returns 0 if the shell executed OK, something else if
+ * we couldn't start shell or it exited badly.
+ */
+spawncli(f, n)
+{
+ extern char *strrchr();
+ register int pid;
+ register int wpid;
+ register int (*oqsig)();
+ register int (*oisig)();
+ int status;
+ int errp = FALSE;
+
+ if (shellp == NULL) {
+ shellp = getenv("SHELL");
+ if (shellp == NULL)
+ shellp = getenv("shell");
+ if (shellp == NULL)
+ shellp = "/bin/sh"; /* Safer. */
+ shname = strrchr( shellp, '/' );
+ shname = shname ? shname++ : shellp;
+
+ }
+ ttcolor(CTEXT);
+ ttnowindow();
+ ttmove(nrow-1, 0);
+ if (epresf != FALSE) {
+ tteeol();
+ epresf = FALSE;
+ }
+ ttclose();
+ sgarbf = TRUE; /* Force repaint. */
+ oqsig = signal(SIGQUIT, SIG_IGN);
+ oisig = signal(SIGINT, SIG_IGN);
+ if ((pid=fork()) == 0) {
+ (void) signal(SIGINT, oisig);
+ (void) signal(SIGQUIT, oqsig);
+ execlp(shellp, shname, "-i", (char *)NULL);
+ _exit(1); /* Should do better! */
+ }
+ else if (pid > 0) {
+ while ((wpid=wait(&status))>=0 && wpid!=pid)
+ ;
+ }
+ else errp = TRUE;
+
+ signal(SIGINT, oisig);
+ signal(SIGQUIT, oqsig);
+ ttopen();
+ setttysize();
+ ttwindow();
+
+ if(errp)
+ ewprintf("Failed to create process");
+
+ return ( errp | status );
+}
+
+/*
+ * Put the tty in normal mode, so he can do a second ^Z. Then
+ * wait for a char. To use ^Z^Z to suspend and "fg %mg CR CR"
+ * to continue;
+ *
+ * Returns 0 if it works, which presumably it must.
+ */
+attachtoparent(f, n)
+{
+ extern char *strrchr();
+ register int pid;
+ register int wpid;
+ register int (*oqsig)();
+ register int (*oisig)();
+ int status;
+ int errp = FALSE;
+ int omask;
+ sigset_t newsig,oldsig;
+
+ ttcolor(CTEXT);
+ ttnowindow();
+ ttmove(nrow-1, 0);
+ if (epresf != FALSE) {
+ tteeol();
+ epresf = FALSE;
+ }
+ ttclose();
+ sgarbf = TRUE; /* Force repaint. */
+#ifdef SIGTSTP
+ sigemptyset(&newsig);
+ sigprocmask(SIG_SETMASK, &newsig, &oldsig);
+ (void) kill(0, SIGTSTP);
+ sigprocmask(SIG_SETMASK, &oldsig, NULL);
+#else
+ getchar();
+#endif
+ ttopen();
+ setttysize();
+ ttwindow();
+
+ return ( 0 );
+}
diff --git a/usr.bin/mg/sysdef.h b/usr.bin/mg/sysdef.h
new file mode 100644
index 00000000000..9b0ac63ee31
--- /dev/null
+++ b/usr.bin/mg/sysdef.h
@@ -0,0 +1,37 @@
+/*
+ * System V system header file
+ */
+#include <stdio.h>
+
+#define KBLOCK 8192 /* Kill grow. */
+#define GOOD 0 /* Good exit status. */
+#define MAXPATH 256 /* Maximum length of path for chdir */
+
+typedef long RSIZE; /* Type for file/region sizes */
+typedef short KCHAR; /* Type for internal keystrokes */
+
+/*
+ * Macros used by the buffer name making code.
+ * Start at the end of the file name, scan to the left
+ * until BDC1 (or BDC2, if defined) is reached. The buffer
+ * name starts just to the right of that location, and
+ * stops at end of string (or at the next BDC3 character,
+ * if defined). BDC2 and BDC3 are mainly for VMS.
+ */
+#define BDC1 '/' /* Buffer names. */
+
+#define MALLOCROUND(m) (m+=7,m&=~7) /* round up to 8 byte boundry */
+
+#define fncmp strcmp /* file name comparison */
+#define bcopy(s,d,n) memcpy(d,s,n) /* memory-to-memory copy */
+#define bzero(s,n) memset(s,0,n) /* clear memory */
+char *getenv();
+#define gettermtype() getenv("TERM") /* determine terminal type */
+char *getcwd();
+#define getwd(cwd) getcwd(cwd,NFILEN) /* get current working dir */
+
+struct fileinfo {
+ unsigned short fi_mode;
+ unsigned short fi_uid;
+ unsigned short fi_gid;
+};
diff --git a/usr.bin/mg/tar.exclude b/usr.bin/mg/tar.exclude
new file mode 100644
index 00000000000..019cfb531d5
--- /dev/null
+++ b/usr.bin/mg/tar.exclude
@@ -0,0 +1,7 @@
+hold
+obj
+sys
+mg.tar
+*.o
+mg
+*~
diff --git a/usr.bin/mg/tty.c b/usr.bin/mg/tty.c
new file mode 100644
index 00000000000..3ec5c434629
--- /dev/null
+++ b/usr.bin/mg/tty.c
@@ -0,0 +1,442 @@
+/*
+ * Termcap/terminfo display driver
+ *
+ * Termcap is a terminal information database and routines to describe
+ * terminals on most UNIX systems. Many other systems have adopted
+ * this as a reasonable way to allow for widly 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 extensivly 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 assuption that scrolling
+ * region operations look better.
+ */
+#include "def.h"
+
+#define BEL 0x07 /* BEL character. */
+
+extern int ttrow;
+extern int ttcol;
+extern int tttop;
+extern int ttbot;
+extern int tthue;
+
+int tceeol; /* Costs are set later */
+int tcinsl;
+int tcdell;
+
+static int insdel; /* Do we have both insert & delete line? */
+
+#ifdef NO_RESIZE
+static setttysize();
+#endif
+
+char *tgetstr();
+char *tgoto();
+int ttputc();
+
+#define TCAPSLEN 1024
+
+char tcapbuf[TCAPSLEN];
+
+/* PC, UP, and BC are used by termlib, so must be extern and have these
+ * names unless you have a non-standard termlib.
+ */
+
+int LI; /* standard # lines */
+char PC,
+ *CM,
+ *CE,
+ *UP,
+ *BC,
+ *IM, /* insert mode */
+ *IC, /* insert a single space */
+ *EI, /* end insert mode */
+ *DC,
+ *AL, /* add line */
+ *DL, /* del line */
+ *pAL, /* parameterized add line */
+ *pDL, /* parameterized delete line */
+ *TI, /* term init -- start using cursor motion */
+ *TE, /* term end --- end using cursor motion */
+ *SO,
+ *SE,
+ *CD,
+ *CS, /* set scroll region */
+ *SF, /* forw index (used with scroll region) */
+ *SR; /* back index (used with scroll region) */
+#ifdef XKEYS
+char *KS, *KE; /* enter keypad mode, exit keypad mode */
+#endif
+int SG; /* number of glitches, 0 for invisible, -1 for none */
+ /* (yes virginia, there are terminals with invisible glitches) */
+
+/*
+ * Initialize the terminal when the editor
+ * gets started up.
+ */
+static char tcbuf[1024];
+
+ttinit() {
+ char *tv_stype;
+ char *t, *p, *tgetstr();
+#ifndef gettermtype /* (avoid declaration if #define) */
+ char *gettermtype(); /* system dependent function to determin terminal type */
+#endif
+
+ if((tv_stype = gettermtype()) == NULL)
+ panic("Could not determine terminal type");
+ if((tgetent(tcbuf, tv_stype)) != 1) {
+ (VOID) strcpy(tcbuf, "Unknown terminal type ");
+ (VOID) strcat(tcbuf, tv_stype);
+ panic(tcbuf);
+ }
+
+ p = tcapbuf;
+ t = tgetstr("pc", &p);
+ if(t) PC = *t;
+
+ LI = tgetnum("li");
+ CD = tgetstr("cd", &p);
+ CM = tgetstr("cm", &p);
+ CE = tgetstr("ce", &p);
+ UP = tgetstr("up", &p);
+ BC = tgetstr("bc", &p);
+ IM = tgetstr("im", &p);
+ IC = tgetstr("ic", &p);
+ EI = tgetstr("ei", &p);
+ DC = tgetstr("dc", &p);
+ AL = tgetstr("al", &p);
+ DL = tgetstr("dl", &p);
+ pAL= tgetstr("AL", &p); /* parameterized insert and del. line */
+ pDL= tgetstr("DL", &p);
+ TI = tgetstr("ti", &p);
+ TE = tgetstr("te", &p);
+ SO = tgetstr("so", &p);
+ SE = tgetstr("se", &p);
+ CS = tgetstr("cs", &p); /* set scrolling region */
+ SF = tgetstr("sf", &p);
+ if(!SF || !*SF) { /* this is what GNU Emacs does */
+ SF = tgetstr("do", &p);
+ if(!SF || !*SF) {
+ SF = tgetstr("nl", &p);
+ if(!SF || !*SF) SF = "\n";
+ }
+ }
+ SR = tgetstr("sr", &p);
+ SG = tgetnum("sg"); /* standout glitch */
+#ifdef XKEYS
+ KS = tgetstr("ks", &p); /* keypad start, keypad end */
+ KE = tgetstr("ke", &p);
+#endif
+
+ if(CM == NULL || UP == NULL)
+ panic("This terminal is to stupid to run MicroGnuEmacs\n");
+ ttresize(); /* set nrow & ncol */
+
+ /* watch out for empty capabilities (sure to be wrong) */
+ if (CE && !*CE) CE = NULL;
+ if (CS && !*CS) CS = NULL;
+ if (SR && !*SR) SR = NULL;
+ if (AL && !*AL) AL = NULL;
+ if (DL && !*DL) DL = NULL;
+ if (pAL && !*pAL) pAL = NULL;
+ if (pDL && !*pDL) pDL = NULL;
+ if (CD && !*CD) CD = NULL;
+
+ if(!CE) tceeol = ncol;
+ else tceeol = charcost(CE);
+
+ /* Estimate cost of inserting a line */
+ if (CS && SR) tcinsl = charcost(CS)*2 + charcost(SR);
+ else if (pAL) tcinsl = charcost(pAL);
+ else if (AL) tcinsl = charcost(AL);
+ else tcinsl = NROW * NCOL; /* make this cost high enough */
+
+ /* Estimate cost of deleting a line */
+ if (CS) tcdell = charcost(CS)*2 + charcost(SF);
+ else if (pDL) tcdell = charcost(pDL);
+ else if (DL) tcdell = charcost(DL);
+ else tcdell = NROW * NCOL; /* make this cost high enough */
+
+ /* Flag to indicate that we can both insert and delete lines */
+ insdel = (AL || pAL) && (DL || pDL);
+
+ if (p >= &tcapbuf[TCAPSLEN])
+ panic("Terminal description too big!\n");
+ if (TI && *TI) putpad(TI, 1); /* init the term */
+}
+
+/*
+ * 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.
+ */
+tttidy() {
+ if (TE && *TE) putpad(TE, 1); /* set the term back to normal mode */
+#ifdef XKEYS
+ ttykeymaptidy();
+#endif
+}
+
+/*
+ * 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!
+ */
+ttmove(row, col) {
+ char *tgoto();
+
+ if (ttrow!=row || ttcol!=col) {
+ putpad(tgoto(CM, col, row), 1);
+ ttrow = row;
+ ttcol = col;
+ }
+}
+
+/*
+ * Erase to end of line.
+ */
+tteeol() {
+ if(CE) putpad(CE, 1);
+ else {
+ register int i=ncol-ttcol;
+ while(i--) ttputc(' ');
+ ttrow = ttcol = HUGE;
+ }
+}
+
+/*
+ * Erase to end of page.
+ */
+tteeop() {
+ if(CD) putpad(CD, nrow - ttrow);
+ else {
+ putpad(CE, 1);
+ if (insdel) ttdell(ttrow + 1, LI, LI - ttrow - 1);
+ else { /* do it by hand */
+ register int line;
+ for (line = ttrow + 1; line <= LI; ++line) {
+ ttmove(line, 0);
+ tteeol();
+ }
+ }
+ ttrow = ttcol = HUGE;
+ }
+}
+
+/*
+ * Make a noise.
+ */
+ttbeep() {
+ ttputc(BEL);
+ 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 no scrolling region, use a set
+ * of insert and delete line sequences
+ */
+ttinsl(row, bot, nchunk) {
+ register int i, nl;
+
+ if (row == bot) { /* Case of one line insert is */
+ ttmove(row, 0); /* special */
+ tteeol();
+ return;
+ }
+ if (CS && SR) { /* Use scroll region and back index */
+ nl = bot - row;
+ ttwindow(row,bot);
+ ttmove(row, 0);
+ while (nchunk--) putpad(SR, nl);
+ ttnowindow();
+ return;
+ } else if (insdel) {
+ ttmove(1+bot-nchunk, 0);
+ nl = nrow - ttrow;
+ if (pDL) putpad(tgoto(pDL, 0, nchunk), nl);
+ else for (i=0; i<nchunk; i++) /* For all lines in the chunk */
+ putpad(DL, nl);
+ ttmove(row, 0);
+ nl = nrow - ttrow; /* ttmove() changes ttrow */
+ if (pAL) putpad(tgoto(pAL, 0, nchunk), nl);
+ else for (i=0; i<nchunk; i++) /* For all lines in the chunk */
+ putpad(AL, 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 a crafty sequences of insert and delete
+ * lines. The presence of the echo area makes a
+ * boundry condition go away.
+ */
+ttdell(row, bot, nchunk)
+{
+ register int i, nl;
+
+ if (row == bot) { /* One line special case */
+ ttmove(row, 0);
+ tteeol();
+ return;
+ }
+ if (CS) { /* scrolling region */
+ nl = bot - row;
+ ttwindow(row, bot);
+ ttmove(bot, 0);
+ while (nchunk--) putpad(SF, nl);
+ ttnowindow();
+ }
+ else if(insdel) {
+ ttmove(row, 0); /* Else use insert/delete line */
+ nl = nrow - ttrow;
+ if (pDL) putpad(tgoto(pDL, 0, nchunk), nl);
+ else for (i=0; i<nchunk; i++) /* For all lines in the chunk */
+ putpad(DL, nl);
+ ttmove(1+bot-nchunk,0);
+ nl = nrow - ttrow; /* ttmove() changes ttrow */
+ if (pAL) putpad(tgoto(pAL, 0, nchunk), nl);
+ else for (i=0; i<nchunk; i++) /* For all lines in the chunk */
+ putpad(AL, 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 pathalogical 1 line scroll window that
+ * 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).
+ *
+ */
+ttwindow(top, bot)
+{
+ if (CS && (tttop!=top || ttbot!=bot)) {
+ putpad(tgoto(CS, 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 is suspends the
+ * editor, and by "display.c" when it is getting ready
+ * to exit. This function gets to full screen scroll
+ * by telling the terminal to set a scrolling regin
+ * that is LI or nrow rows high, whichever is larger.
+ * This behavior seems to work right on systems
+ * where you can set your terminal size.
+ */
+ttnowindow()
+{
+ if (CS) {
+ putpad(tgoto(CS, (nrow > LI ? nrow : LI) - 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.
+ */
+ttcolor(color) register int color; {
+ if (color != tthue) {
+ if (color == CTEXT) { /* Normal video. */
+ putpad(SE, 1);
+ } else if (color == CMODE) { /* Reverse video. */
+ putpad(SO, 1);
+ }
+ tthue = color; /* Save the color. */
+ }
+}
+
+/*
+ * This routine is called by the
+ * "refresh the screen" command to try and resize
+ * the display. The new size, which must be deadstopped
+ * to not exceed the NROW and NCOL limits, it stored
+ * back into "nrow" and "ncol". Display can always deal
+ * with a screen NROW by NCOL. Look in "window.c" to
+ * see how the caller deals with a change.
+ */
+ttresize() {
+ setttysize(); /* found in "ttyio.c", */
+ /* ask OS for tty size */
+ if (nrow < 1) /* Check limits. */
+ nrow = 1;
+ else if (nrow > NROW)
+ nrow = NROW;
+ if (ncol < 1)
+ ncol = 1;
+ else if (ncol > NCOL)
+ ncol = NCOL;
+}
+
+#ifdef NO_RESIZE
+static setttysize() {
+ nrow = tgetnum("li");
+ ncol = tgetnum("co");
+}
+#endif
+
+static int cci;
+
+/*ARGSUSED*/
+static int /* fake char output for charcost() */
+fakec(c)
+char c;
+{
+ cci++;
+}
+
+/* calculate the cost of doing string s */
+charcost (s) char *s; {
+ cci = 0;
+
+ tputs(s, nrow, fakec);
+ return cci;
+}
diff --git a/usr.bin/mg/ttydef.h b/usr.bin/mg/ttydef.h
new file mode 100644
index 00000000000..3700ea0f174
--- /dev/null
+++ b/usr.bin/mg/ttydef.h
@@ -0,0 +1,25 @@
+/*
+ * Termcap terminal file, nothing special, just make it big
+ * enough for windowing systems.
+ */
+
+#define GOSLING /* Compile in fancy display. */
+/* #define MEMMAP */ /* Not memory mapped video. */
+
+#define NROW 66 /* (maximum) Rows. */
+#define NCOL 132 /* (maximum) Columns. */
+/* #define MOVE_STANDOUT /* don't move in standout mode */
+#define STANDOUT_GLITCH /* possible standout glitch */
+#define TERMCAP /* for possible use in ttyio.c */
+
+#define getkbd() (ttgetc())
+
+#ifndef XKEYS
+#define ttykeymapinit() {}
+#endif
+
+extern int tputs();
+#define putpad(str, num) tputs(str, num, ttputc)
+
+#define KFIRST K00
+#define KLAST K00
diff --git a/usr.bin/mg/ttyio.c b/usr.bin/mg/ttyio.c
new file mode 100644
index 00000000000..c3ddf420ef9
--- /dev/null
+++ b/usr.bin/mg/ttyio.c
@@ -0,0 +1,265 @@
+/*
+ * Name: MicroEMACS
+ * System V terminal I/O.
+ * Version: 0
+ * Last edit: Tue Aug 26 23:57:57 PDT 1986
+ * By: gonzo!daveb
+ * {sun, amdahl, mtxinu}!rtech!gonzo!daveb
+ *
+ * The functions in this file
+ * negotiate with the operating system for
+ * keyboard characters, and write characters to
+ * the display in a barely buffered fashion.
+ *
+ * This version goes along with tty/termcap/tty.c.
+ * Terminal size is determined there, rather than here, and
+ * this does not open the termcap file
+ */
+#include "def.h"
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <termio.h>
+
+#define NOBUF 512 /* Output buffer size. */
+
+char obuf[NOBUF]; /* Output buffer. */
+int nobuf; /* buffer count */
+
+static struct termio ot; /* entry state of the terminal */
+static struct termio nt; /* editor's terminal state */
+
+static int ttyactivep = FALSE; /* terminal in editor mode? */
+static int ttysavedp = FALSE; /* terminal state saved? */
+
+int nrow; /* Terminal size, rows. */
+int ncol; /* Terminal size, columns. */
+
+/* These are used to implement typeahead on System V */
+
+int kbdflgs; /* saved keyboard fd flags */
+int kbdpoll; /* in O_NDELAY mode */
+int kbdqp; /* there is a char in kbdq */
+char kbdq; /* char we've already read */
+
+/*
+ * This function gets called once, to set up
+ * the terminal channel. This version turns off flow
+ * control. This may be wrong for your system, but no
+ * good solution has really been found (daveb).
+ */
+ttopen()
+{
+ register char *cp;
+ extern char *getenv();
+
+ if (ttyactivep)
+ return;
+
+ if( !ttysavedp )
+ {
+ if (ioctl(0, TCGETA, &ot) < 0)
+ abort();
+ nt = ot; /* save entry state */
+ nt.c_cc[VMIN] = 1; /* one character read is OK */
+ nt.c_cc[VTIME] = 0; /* Never time out. */
+ nt.c_iflag |= IGNBRK;
+ nt.c_iflag &= ~( ICRNL | INLCR | ISTRIP | IXON | IXOFF );
+ nt.c_oflag &= ~OPOST;
+ nt.c_cflag |= CS8; /* allow 8th bit on input */
+ nt.c_cflag &= ~PARENB; /* Don't check parity */
+ nt.c_lflag &= ~( ECHO | ICANON | ISIG );
+
+ kbdpoll = (((kbdflgs = fcntl(0, F_GETFL, 0)) & O_NDELAY) != 0);
+
+ ttysavedp = TRUE;
+ }
+
+ if (ioctl(0, TCSETAF, &nt) < 0)
+ abort();
+
+ /* This really belongs in tty/termcap... */
+
+ if ((cp=getenv("TERMCAP")) == NULL
+ || (nrow=getvalue(cp, "li")) <= 0
+ || (ncol=getvalue(cp, "co")) <= 0) {
+ nrow = 24;
+ ncol = 80;
+ }
+ if (nrow > NROW) /* Don't crash if the */
+ nrow = NROW; /* termcap entry is */
+ if (ncol > NCOL) /* too big. */
+ ncol = NCOL;
+
+ ttyactivep = TRUE;
+}
+
+/*
+ * This routine scans a string, which is
+ * actually the return value of a getenv call for the TERMCAP
+ * variable, looking for numeric parameter "name". Return the value
+ * if found. Return -1 if not there. Assume that "name" is 2
+ * characters long. This limited use of the TERMCAP lets us find
+ * out the size of a window on the X display.
+ */
+getvalue(cp, name)
+register char *cp;
+register char *name;
+{
+ for (;;) {
+ while (*cp!=0 && *cp!=':')
+ ++cp;
+ if (*cp++ == 0) /* Not found. */
+ return (-1);
+ if (cp[0]==name[0] && cp[1]==name[1] && cp[2]=='#')
+ return (atoi(cp+3)); /* Stops on ":". */
+ }
+}
+
+/*
+ * This function gets called just
+ * before we go back home to the shell. Put all of
+ * the terminal parameters back.
+ */
+ttclose()
+{
+ if(!ttysavedp || !ttyactivep)
+ return;
+ ttflush();
+ if (ioctl(0, TCSETAF, &ot) < 0 || fcntl( 0, F_SETFL, kbdflgs ) < 0)
+ abort();
+ ttyactivep = FALSE;
+}
+
+/*
+ * Write character to the display.
+ * Characters are buffered up, to make things
+ * a little bit more efficient.
+ */
+ttputc(c)
+{
+ if (nobuf >= NOBUF)
+ ttflush();
+ obuf[nobuf++] = c;
+}
+
+/*
+ * Flush output.
+ */
+ttflush()
+{
+ if (nobuf != 0) {
+ write(1, obuf, nobuf);
+ nobuf = 0;
+ }
+}
+
+/*
+ * Read character from terminal.
+ * All 8 bits are returned, so that you can use
+ * a multi-national terminal.
+ *
+ * If keyboard 'queue' already has typeahead from a typeahead() call,
+ * just return it. Otherwise, make sure we are in blocking i/o mode
+ * and read a character.
+ */
+ttgetc()
+{
+ if( kbdqp )
+ kbdqp = FALSE;
+ else
+ {
+ if( kbdpoll && fcntl( 0, F_SETFL, kbdflgs ) < 0 )
+ abort();
+ kbdpoll = FALSE;
+ while (read(0, &kbdq, 1) != 1)
+ ;
+ }
+ return ( kbdq & 0xff );
+}
+
+/*
+ * Return non-FALSE if typeahead is pending.
+ *
+ * If already got unread typeahead, do nothing.
+ * Otherwise, set keyboard to O_NDELAY if not already, and try
+ * a one character read.
+ */
+typeahead()
+{
+ if( !kbdqp )
+ {
+ if( !kbdpoll && fcntl( 0, F_SETFL, kbdflgs | O_NDELAY ) < 0 )
+ abort();
+ kbdpoll = TRUE;
+ kbdqp = (1 == read( 0, &kbdq, 1 ));
+ }
+ return ( kbdqp );
+}
+
+
+/*
+ * panic: print error and die, leaving core file.
+ * Don't know why this is needed (daveb).
+ */
+panic(s)
+char *s;
+{
+ fprintf(stderr, "%s\r\n", s);
+ abort();
+}
+
+
+/*
+** This should check the size of the window, and reset if needed.
+*/
+
+setttysize()
+{
+#ifdef TIOCGWINSZ
+ struct winsize winsize;
+ if (ioctl(0, TIOCGWINSZ, (char *) &winsize) == 0) {
+ nrow = winsize . ws_row;
+ ncol = winsize . ws_col;
+ } else
+#endif
+ if ((nrow=tgetnum ("li")) <= 0
+ || (ncol=tgetnum ("co")) <= 0) {
+ nrow = 24;
+ ncol = 80;
+ }
+ if (nrow > NROW) /* Don't crash if the */
+ nrow = NROW; /* termcap entry is */
+ if (ncol > NCOL) /* too big. */
+ ncol = NCOL;
+}
+
+#ifndef NO_DPROMPT
+#include <signal.h>
+#include <setjmp.h>
+
+static jmp_buf tohere;
+
+static void alrm()
+{
+ longjmp(tohere, -1);
+}
+
+/*
+ * Return TRUE if we wait without doing anything, else return FALSE.
+ */
+
+ttwait()
+{
+ int alrm();
+
+ if (kbdqp)
+ return FALSE; /* already pending input */
+ if (setjmp(tohere))
+ return TRUE; /* timeout on read if here */
+ signal(SIGALRM, alrm); alarm(2);
+ kbdqp = (1 == read(0, &kbdq, 1));
+ alarm(0);
+ return FALSE; /* successful read if here */
+}
+#endif NO_DPROMPT
diff --git a/usr.bin/mg/ttykbd.c b/usr.bin/mg/ttykbd.c
new file mode 100644
index 00000000000..13c0198a080
--- /dev/null
+++ b/usr.bin/mg/ttykbd.c
@@ -0,0 +1,55 @@
+/*
+ * Name: MG 2a
+ * Termcap keyboard driver using key files
+ * Created: 22-Nov-1987 Mic Kaczmarczik (mic@emx.cc.utexas.edu)
+ */
+
+#include "def.h"
+#ifdef XKEYS
+
+/*
+ * Get keyboard character. Very simple if you use keymaps and keys files.
+ * Bob was right -- the old XKEYS code is not the right solution.
+ * FKEYS code is not usefull other than to help debug FKEYS code in
+ * extend.c.
+ */
+
+#ifdef FKEYS
+char *keystrings[] = { NULL } ;
+#endif
+
+/*
+ * Turn on function keys using KS, 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.
+ */
+extern int ttputc();
+
+ttykeymapinit()
+{
+ extern char *KS;
+#ifndef NO_STARTUP
+ char *cp, *startupfile();
+
+ if (cp = gettermtype()) {
+ if (((cp = startupfile(cp)) != NULL)
+ && (load(cp) != TRUE))
+ ewprintf("Error reading key initialization file");
+ }
+#endif
+ if (KS && *KS) /* turn on keypad */
+ putpad(KS, 1);
+}
+
+/*
+ * Clean up the keyboard -- called by tttidy()
+ */
+ttykeymaptidy()
+{
+ extern char *KE;
+
+ if (KE && *KE)
+ putpad(KE, 1); /* turn off keypad */
+}
+
+#endif
diff --git a/usr.bin/mg/tutorial b/usr.bin/mg/tutorial
new file mode 100644
index 00000000000..d623febf889
--- /dev/null
+++ b/usr.bin/mg/tutorial
@@ -0,0 +1,604 @@
+Copyright (c) 1985 Richard M. Stallman. See end for copying conditions.
+
+You are looking at the Emacs tutorial.
+
+Emacs commands generally involve the CONTROL key or the META (ESC)
+key. Rather than write out META or CONTROL each time we want you to
+prefix a character, we'll use the following abbreviations:
+
+ C-<chr> means hold the CONTROL key while typing the character <chr>
+ Thus, C-f would be: hold the CONTROL key and type f.
+ M-<chr> means type <ESC>, release it, then type the character <chr>.
+
+The characters ">>" at the left margin indicate directions for you to
+try using a command. For instance:
+
+>> Now type C-v (View next screen) to move to the next screen.
+ (go ahead, do it by depressing the control key and v together).
+ From now on, you'll be expected to do this whenever you finish
+ reading the screen.
+
+Note that there is an overlap when going from screen to screen; this
+provides some continuity when moving through the file.
+
+The first thing that you need to know is how to move around from
+place to place in the file. You already know how to move forward a
+screen, with C-v. To move backwards a screen, type M-v (type <ESC>v).
+
+>> Try typing M-v and then C-v to move back and forth a few times.
+
+
+SUMMARY
+-------
+
+The following commands are useful for viewing screenfuls:
+
+ C-v Move forward one screenful
+ M-v Move backward one screenful
+ C-l Clear screen and redisplay everything
+ putting the text near the cursor at the center.
+ (That's control-L, not control-1.
+ There is no such character as control-1.)
+
+>> Find the cursor and remember what text is near it.
+ Then type a C-l.
+ Find the cursor again and see what text is near it now.
+
+
+BASIC CURSOR CONTROL
+--------------------
+
+Getting from screenful to screenful is useful, but how do you
+reposition yourself within a given screen to a specific place? There
+are several ways you can do this. One way (not the best, but the most
+basic) is to use the commands previous, backward, forward and next.
+As you can imagine these commands (which are given to Emacs as C-p,
+C-b, C-f, and C-n respectively) move the cursor from where it
+currently is to a new place in the given direction. It is also
+possible to move the cursor with the arrow keys, but this requires you
+move your hand from the keyboard, it is also not supported on other
+machines that do support Emacs. Emacs runs on everything from a CP/M
+machine to large mainframes. Here then, in a more graphical form are
+the commands:
+
+ Previous line, C-p
+ :
+ :
+ Backward, C-b .... Current cursor position .... Forward, C-f
+ :
+ :
+ Next line, C-n
+
+>> Move the cursor to the line in the middle of that diagram
+ and type C-l to see the whole diagram centered in the screen.
+
+You'll probably find it easy to think of these by letter. P for
+previous, N for next, B for backward and F for forward. These are
+the basic cursor positioning commands and you'll be using them ALL
+the time so it would be of great benefit if you learn them now.
+
+>> Do a few C-n's to bring the cursor down to this line.
+
+>> Move into the line with C-f's and then up with C-p's.
+ See what C-p does when the cursor is in the middle of the line.
+
+>> Try to C-b at the beginning of a line. Do a few more C-b's.
+ Then do C-f's back to the end of the line and beyond.
+
+When you go off the top or bottom of the screen, the text beyond
+the edge is shifted onto the screen so that your instructions can
+be carried out while keeping the cursor on the screen.
+
+>> Try to move the cursor off the bottom of the screen with C-n and
+ see what happens.
+
+If moving by characters is too slow, you can move by words. M-f
+(ESC-f) moves forward a word and M-b moves back a word.
+
+>> Type a few M-f's and M-b's. Intersperse them with C-f's and C-b's.
+
+Notice the parallel between C-f and C-b on the one hand, and M-f and
+M-b on the other hand. Very often Meta characters are used for
+operations related to English text whereas Control characters operate
+on the basic textual units that are independent of what you are
+editing (characters, lines, etc). C-a and C-e move to the beginning or
+end of a line.
+
+>> Try a couple of C-a's, and then a couple of C-e's.
+ See how repeated C-a's do nothing.
+
+Two other simple cursor motion commands are M-< (Meta Less-than),
+which moves to the beginning of the file, and M-> (Meta Greater-than),
+which moves to the end of the file. You probably don't need to try
+them, since finding this spot again will be boring. On most terminals
+the "<" is above the comma and you must use the shift key to type it.
+On these terminals you must use the shift key to type M-< also;
+without the shift key, you would be typing M-comma.
+
+The location of the cursor in the text is also called "point". To
+paraphrase, the cursor shows on the screen where point is located in
+the text.
+
+Here is a summary of simple moving operations including the word and
+sentence moving commands:
+
+ C-f Move forward a character
+ C-b Move backward a character
+
+ M-f Move forward a word
+ M-b Move backward a word
+
+ c-n Move to next line
+ C-p Move to previous line
+
+ C-a Move to beginning of line
+ C-e Move to end of line
+
+ M-< Go to beginning of file
+ M-> Go to end of file
+
+>> Try all of these commands now a few times for practice.
+ Since the last two will take you away from this screen,
+ you can come back here with M-v's and C-v's. These are
+ the most often used commands.
+
+Like all other commands in Emacs, these commands can be given
+arguments which cause them to be executed repeatedly. The way you
+give a command a repeat count is by typing C-u and then the digits
+before you type the command.
+
+For instance, C-u 8 C-f moves forward eight characters.
+
+>> Try giving a suitable argument to C-n or C-p to come as close
+ as you can to this line in one jump.
+
+The only apparent exception to this is the screen moving commands,
+C-v and M-v. When given an argument, they scroll the screen up or
+down by that many lines, rather than screenfuls. This proves to be
+much more useful.
+
+>> Try typing C-u 8 C-v now.
+
+Did it scroll the screen up by 8 lines? If you would like to
+scroll it down you can give an argument to M-v.
+
+
+WHEN EMACS IS HUNG
+-----------------
+
+If Emacs gets into an infinite (or simply very long) computation which
+you don't want to finish, you can stop it safely by typing C-g.
+You can also use C-g to discard a numeric argument or the beginning of
+a command that you don't want to finish.
+
+>> Type C-u 100 to make a numeric arg of 100, then type C-g.
+ Now type C-f. How many characters does it move?
+ If you have typed an <ESC> by mistake, you can get rid of it
+ with a C-g.
+
+WINDOWS
+-------
+
+Emacs can have several windows, each displaying its own text.
+At this stage it is better not to go into the techniques of
+using multiple windows. But you do need to know how to get
+rid of extra windows that may appear to display help or
+output from certain commands. It is simple:
+
+ C-x 1 One window (i.e., kill all other windows).
+
+That is Control-x followed by the digit 1.
+C-x 1 makes the window which the cursor is in become
+the full screen, by getting rid of any other windows.
+
+>> Move the cursor to this line and type C-l (Control-L).
+>> Type M-x. The cursor will move to the bottom of the screen.
+>> Type the words "describe-bindings" and hit return.
+ See how this window shrinks, while a new one appears
+ to display which functions are connected to which keys.
+
+>> Type C-x 1 and see the documentation listing window disappear.
+
+
+INSERTING AND DELETING
+----------------------
+
+If you want to insert text, just type it. Characters which you can
+see, such as A, 7, *, etc. are taken by Emacs as text and inserted
+immediately. Type <Return> (the carriage-return key) to insert a
+Newline character.
+
+You can delete the last character you typed by typing <DEL>. More
+generally, <DEL> deletes the character immediately before the current
+cursor position.
+
+>> Do this now, type a few characters and then delete them
+ by typing <DEL> a few times. Don't worry about this file
+ being changed; you won't affect the master tutorial. This is just
+ a copy of it.
+
+>> Now start typing text until you reach the right margin, and keep
+ typing. When a line of text gets too big for one line on the
+ screen, the line of text is "continued" off the edge of the screen.
+ The dollar sign at the right margin indicates a line which has
+ been continued.
+>> Use <DEL>s to delete the text until the line fits on one screen
+ line again. The continuation mark goes away.
+
+>> Move the cursor to the beginning of a line and type <DEL>. This
+ deletes the newline before the line and merges the line onto
+ the previous line. The resulting line may be too long to fit, in
+ which case it has a continuation mark.
+>> Type <Return> to reinsert the Newline you deleted.
+
+Remember that most Emacs commands can be given a repeat count;
+this includes characters which insert themselves.
+
+>> Try that now -- type C-u 8 * and see what happens.
+
+You've now learned the most basic way of typing something in
+Emacs and correcting errors. You can delete by words or lines
+as well. Here is a summary of the delete operations:
+
+ <DEL> delete the character just before the cursor
+ C-d delete the next character after the cursor
+
+ M-<DEL> kill the word immediately before the cursor
+ M-d kill the next word after the cursor
+
+ C-k kill from the cursor position to end of line
+
+Notice that <DEL> and C-d vs M-<DEL> and M-d extend the parallel
+started by C-f and M-f (well, <DEL> isn't really a control
+character, but let's not worry about that).
+
+Now suppose you kill something, and then you decide that you want to
+get it back? Well, whenever you kill something bigger than a
+character, Emacs saves it for you. To yank it back, use C-y. You
+can kill text in one place, move elsewhere, and then do C-y; this is
+a good way to move text around. Note that the difference
+between "Killing" and "Deleting" something is that "Killed" things
+can be yanked back, and "Deleted" things cannot. Generally, the
+commands that can destroy a lot of text save it, while the ones that
+attack only one character, or nothing but blank lines and spaces, do
+not save.
+
+For instance, type C-n a couple times to postion the cursor
+at some line on this screen.
+
+>> Do this now, move the cursor and kill that line with C-k.
+
+Note that a single C-k kills the contents of the line, and a second
+C-k kills the line itself, and make all the other lines move up. If
+you give C-k a repeat count, it kills that many lines AND their
+contents.
+
+The text that has just disappeared is saved so that you can
+retrieve it. To retrieve the last killed text and put it where
+the cursor currently is, type C-y.
+
+>> Try it; type C-y to yank the text back.
+
+Think of C-y as if you were yanking something back that someone
+took away from you. Notice that if you do several C-k's in a row
+the text that is killed is all saved together so that one C-y will
+yank all of the lines.
+
+>> Do this now, type C-k several times.
+
+Now to retrieve that killed text:
+
+>> Type C-y. Then move the cursor down a few lines and type C-y
+ again. You now see how to copy some text.
+
+
+FILES
+-----
+
+In order to make the text you edit permanent, you must put it in a
+file. Otherwise, it will go away when your invocation of Emacs goes
+away. You put your editing in a file by "finding" the file. What
+finding means is that you see the contents of the file in your Emacs;
+and, loosely speaking, what you are editing is the file itself.
+However, the changes still don't become permanent until you "save" the
+file. This is so you can have control to avoid leaving a half-changed
+file around when you don't want to.
+
+If you look near the bottom of the screen you will see a line that
+begins and ends with dashes, and contains the string:
+ "Mg: TUTORIAL"
+Your copy of the Emacs tutorial is called "TUTORIAL". Whatever
+file you find, that file's name will appear in that precise
+spot.
+
+The commands for finding and saving files are unlike the other
+commands you have learned in that they consist of two characters.
+They both start with the character Control-x. There is a whole series
+of commands that start with Control-x; many of them have to do with
+files, buffers, and related things, and all of them consist of
+Control-x followed by some other character.
+
+Another thing about the command for finding a file is that you have
+to say what file name you want. We say the command "reads an argument
+from the terminal" (in this case, the argument is the name of the
+file). After you type the command
+
+ C-x C-f Find a file
+
+Emacs asks you to type the file name. It echoes on the bottom line of
+the screen. When you type <Return> to end the file name it disappears.
+
+>> Type C-x C-f, then type C-g. This cancels the C-x C-f command
+ that was using the minibuffer. So you do not find any file.
+
+In a little while the file contents appear on the screen. You can
+edit the contents. When you wish to make the changes permanent,
+issue the command
+
+ C-x C-s Save the file
+
+The contents of Emacs are written into the file.
+
+When saving is finished, Emacs prints the name of the file written.
+You should save fairly often, so that you will not lose very much
+work if the system should crash.
+
+>> Type C-x C-s, saving your copy of the tutorial.
+ This should print "Wrote TUTORIAL" at the bottom of the screen.
+
+To make a new file, just find it "as if" it already existed. Then
+start typing in the text. When you ask to "save" the file, Emacs
+will really create the file with the text that you have inserted.
+>From then on, you can consider yourself to be editing an already
+existing file.
+
+
+BUFFERS
+-------
+
+If you find a second file with C-x C-f, the first file remains inside
+Emacs. This way you can get quite a number of files inside Emacs.
+
+The object inside Emacs which holds the text read from one file
+is called a "buffer." Finding a file makes a new buffer inside Emacs.
+To see a list of the buffers that exist in Emacs, type
+
+ C-x C-b List buffers
+
+>> Try C-x C-b now.
+
+See how each buffer has a name, and it may also have a file name
+for the file whose contents it holds. Some buffers do not correspond
+to files. For example, the buffer named "*Buffer List*" does
+not have any file. It is the buffer which contains the buffer
+list that was made by C-x C-b. ANY text you see in an Emacs window
+has to be in some buffer.
+
+>> Type C-x 1 to get rid of the buffer list.
+
+If you make changes to the text of one file, then find another file,
+this does not save the first file. Its changes remain inside Emacs,
+in that file's buffer. The creation or editing of the second file's
+buffer has no effect on the first file's buffer. This is very useful,
+but it also means that you need a convenient way to save the first
+file's buffer. It would be a nuisance to have to switch back to
+it with C-x C-f in order to save it with C-x C-s. So we have
+
+ C-x s Save some buffers
+
+C-x s goes through the list of all the buffers you have
+and finds the ones that contain files you have changed.
+For each such buffer, C-x s asks you whether to save it.
+
+
+EXTENDING THE COMMAND SET
+-------------------------
+
+There are many, many more Emacs commands than could possibly be put
+on all the control and meta characters. Emacs gets around this with
+the X (eXtend) command. This comes in two flavors:
+
+ C-x Character eXtend. Followed by one character.
+ M-x Named command eXtend. Followed by a long name.
+
+These are commands that are generally useful but used less than the
+commands you have already learned about. You have already seen two
+of them: the file commands C-x C-f to Find and C-x C-s to Save.
+Another example is the command to tell Emacs that you'd like to stop
+editing and get rid of Emacs. The command to do this is C-x C-c.
+(Don't worry; it offers to save each changed file before it kills the
+Emacs.)
+
+C-z is the usual way to exit Emacs, because it is always better not to
+kill the Emacs if you are going to do any more editing. On systems
+which allow it, C-z exits from Emacs to a CLI but does not destroy the
+Emacs; you can resume editing by ending that CLI or depth arranging.
+
+You would use C-x C-c if you were running out of memory. You would
+also use it to exit an Emacs invoked under mail handling programs and
+other random utilities, since they may not believe you have really
+finished using the Emacs if it continues to exist.
+
+There are many C-x commands. The ones you know are:
+
+ C-x C-f Find file.
+ C-x C-s Save file.
+ C-x C-b List buffers.
+ C-x C-c Quit Emacs.
+
+Named eXtended commands are commands which are used even less
+frequently, or commands which are used only in certain modes. These
+commands are usually called "functions". An example is the function
+replace-string, which globally replaces one string with another. When
+you type M-x, Emacs prompts you at the bottom of the screen with
+M-x and you should type the name of the function you wish to call; in
+this case, "query-replace". Just type "que<TAB>" and Emacs will
+complete the name. End the command name with <Return>.
+Then type the two "arguments"--the string to be replaced, and the string
+to replace it with--each one ended with a Return.
+
+>> Move the cursor to the blank line two lines below this one.
+ Then type M-x repl s<Return>changed<Return>altered<Return>.
+
+ Notice how this line has changed: you've replaced
+ the word c-h-a-n-g-e-d with "altered" wherever it occured
+ after the cursor.
+
+
+MODE LINE
+---------
+
+If Emacs sees that you are typing commands slowly it shows them to you
+at the bottom of the screen in an area called the "echo area." The echo
+area contains the bottom line of the screen. The line immediately above
+it is called the MODE LINE. The mode line says something like
+
+--**-Mg: TUTORIAL (fundamental)------------------------
+
+This is a very useful "information" line.
+
+The stars near the front mean that you have made changes to the text.
+Right after you visit or save a file, there are no stars, just dashes.
+
+The part of the mode line inside the parentheses is to tell you what
+modes you are in. The default mode is fundamental which is what you
+are in now. It is an example of a "mode". There are several modes in
+Emacs for editing different styles of text, such as indent, bsmap,
+fill, etc. Each mode makes a few commands behave differently.
+
+One mode which is very useful, especially for editing English text, is
+Auto Fill mode. When this mode is on, Emacs breaks the line in
+between words automatically whenever the line gets too long. You can
+turn this mode on by doing M-x auto-fill-mode<Return>. When the mode
+is on, you can turn it off by doing M-x auto-fill-mode<Return>.
+
+>> Type M-x auto-fill-mode<Return> now. Then insert a line of "asdf "
+ over again until you see it divide into two lines. You must put in
+ spaces between them because Auto Fill breaks lines only at spaces.
+
+The margin is usually set at 70 characters, but you can change it
+with the C-x f command. You should give the margin setting you want
+as a numeric argument.
+
+>> Type C-x f with an argument of 20. (C-u 2 0 C-x f).
+ Then type in some text and see Emacs fill lines of 20
+ characters with it. Then set the margin back to 70 using
+ C-x f again.
+
+If you make changes in the middle of a paragraph, Auto Fill mode
+does not re-fill it for you.
+To re-fill the paragraph, type M-q (Meta-q) with the cursor inside
+that paragraph.
+
+>> Move the cursor into the previous paragraph and type M-q.
+
+SEARCHING
+---------
+
+Emacs can do searches for strings (these are groups of contiguous
+characters or words) either forward through the file or backward
+through it. To search for the string means that you are trying to
+locate it somewhere in the file and have Emacs show you where the
+occurrences of the string exist. This type of search is somewhat
+different from what you may be familiar with. It is a search that is
+performed as you type in the thing to search for. The command to
+initiate a search is C-s for forward search, and C-r for reverse
+search. BUT WAIT! Don't do them now. When you type C-s you'll
+notice that the string "I-search" appears as a prompt in the echo
+area. This tells you that Emacs is in what is called an incremental
+search waiting for you to type the thing that you want to search for.
+<ESC> terminates a search.
+
+>> Now type C-s to start a search. SLOWLY, one letter at a time,
+ type the word 'cursor', pausing after you type each
+ character to notice what happens to the cursor.
+>> Type C-s to find the next occurrence of "cursor".
+>> Now type <DEL> four times and see how the cursor moves.
+>> Type <ESC> to terminate the search.
+
+Did you see what happened? Emacs, in an incremental search, tries to
+go to the occurrence of the string that you've typed out so far. To go
+to the next occurrence of 'cursor' just type C-s again. If no such
+occurrence exists Emacs beeps and tells you that it is a failing
+search. C-g would also terminate the search.
+
+If you are in the middle of an incremental search and type <DEL>,
+you'll notice that the last character in the search string is erased
+and the search backs up to the last place of the search. For
+instance, suppose you currently have typed 'cu' and you see that your
+cursor is at the first occurrence of 'cu'. If you now type <DEL>,
+the 'u' on the search line is erased and you'll be repositioned in the
+text to the occurrence of 'c' where the search took you before you
+typed the 'u'. This provides a useful means for backing up while you
+are searching.
+
+If you are in the middle of a search and happen to type a control
+character (other than a C-s or C-r, which tell Emacs to search for the
+next occurrence of the string), the search is terminated.
+
+The C-s starts a search that looks for any occurrence of the search
+string AFTER the current cursor position. But what if you want to
+search for something earlier in the text? To do this, type C-r for
+Reverse search. Everything that applies to C-s applies to C-r except
+that the direction of the search is reversed.
+
+
+GETTING MORE HELP
+-----------------
+
+In this tutorial we have tried to supply just enough information to
+get you started using Emacs. There is so much available in Emacs that
+it would be impossible to explain it all here. However, you may want
+to learn more about Emacs since it has numerous desirable features
+that you don't know about yet.
+
+
+CONCLUSION
+----------
+
+Remember, to exit Emacs permanently use C-x C-c. To exit to a shell
+temporarily, so that you can come back in, use C-z.
+
+This tutorial is meant to be understandable to all new users, so if
+you found something unclear, don't sit and blame yourself - complain!
+
+
+COPYING
+-------
+
+This tutorial, like all of GNU Emacs, is copyrighted, and comes with
+permission to distribute copies on certain conditions:
+
+Copyright (c) 1985 Richard M. Stallman
+
+ Permission is granted to anyone to make or distribute verbatim copies
+ of this document as received, in any medium, provided that the
+ copyright notice and permission notice are preserved,
+ and that the distributor grants the recipient permission
+ for further redistribution as permitted by this notice.
+
+ Permission is granted to distribute modified versions
+ of this document, or of portions of it,
+ under the above conditions, provided also that they
+ carry prominent notices stating who last altered them.
+
+The conditions for copying Emacs itself are slightly different
+but in the same spirit. Please read the file COPYING and then
+do give copies of GNU Emacs to your friends.
+Help stamp out ownership of software by using, writing,
+and sharing free software!
+
+Mg itself is public domain, and may be given away freely. See the
+README file about differences from GNU emacs, and why Mg exists.
+
+*******************************************************************************
+*** This document heavily cut by Randy M. Spencer to apply to ***
+*** Mg written my Mike Meyer and gang. It was released ***
+*** at the AAA users group meeting in Lafayette CA, an Amiga Users ***
+*** Group. My profound thanks to Richard Stallman for his work, I ***
+*** am proud to carry his initials. ***
+*** Additional modifacations were done by Robert A. Larson for Mg ***
+*** version 2a, mainly the name change from MicroGnuEmacs to Mg. ***
+*******************************************************************************
+
+See other files accompanying this for more system specific information.
diff --git a/usr.bin/mg/version.c b/usr.bin/mg/version.c
new file mode 100644
index 00000000000..0f3a9e7562f
--- /dev/null
+++ b/usr.bin/mg/version.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains the string that get written
+ * out by the emacs-version command.
+ */
+
+#define TRUE 1 /* include "def.h" when things get more complicated */
+
+char version[] = "Mg 2a (formerly MicroGnuEmacs)";
+
+/*
+ * Display the version. All this does
+ * is copy the version string onto the echo line.
+ */
+/*ARGSUSED*/
+showversion(f, n)
+int f, n;
+{
+ ewprintf(version);
+ return TRUE;
+}
diff --git a/usr.bin/mg/window.c b/usr.bin/mg/window.c
new file mode 100644
index 00000000000..25f6009ade6
--- /dev/null
+++ b/usr.bin/mg/window.c
@@ -0,0 +1,399 @@
+/*
+ * Window handling.
+ */
+#include "def.h"
+
+/*
+ * 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). If GOSREC is undefined, default is 0, so it acts like GNU.
+ * If GOSREC is defined, with no argument it defaults to 1
+ * and works like in Gosling.
+ */
+/*ARGSUSED*/
+reposition(f, n)
+{
+#ifndef GOSREC
+ curwp->w_force = (f & FFARG) ? (n>=0 ? n+1 : n) : 0;
+#else
+ curwp->w_force = n;
+#endif
+ curwp->w_flag |= WFFORCE;
+ 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.
+ * 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!
+ */
+/*ARGSUSED*/
+refresh(f, n)
+{
+ register WINDOW *wp;
+ register int oldnrow;
+ register int oldncol;
+
+ oldnrow = nrow;
+ oldncol = ncol;
+ ttresize();
+ if (nrow!=oldnrow || ncol!=oldncol) {
+ wp = wheadp; /* Find last. */
+ while (wp->w_wndp != NULL)
+ wp = wp->w_wndp;
+ if (nrow < wp->w_toprow+3) { /* Check if too small. */
+ ewprintf("Display unusable");
+ return (FALSE);
+ }
+ wp->w_ntrows = nrow-wp->w_toprow-2;
+ sgarbf = TRUE;
+ update();
+ ewprintf("New size %d by %d", nrow, ncol);
+ } 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*/
+nextwind(f, n)
+{
+ register WINDOW *wp;
+
+ if ((wp=curwp->w_wndp) == NULL)
+ wp = wheadp;
+ curwp = wp;
+ curbp = wp->w_bufp;
+ return TRUE;
+}
+
+#ifdef GOSMACS
+/* not in Gnu Emacs */
+/*
+ * This command makes the previous
+ * window (previous => up the screen) the
+ * current window. There arn't any errors,
+ * although the command does not do a lot
+ * if there is 1 window.
+ */
+/*ARGSUSED*/
+prevwind(f, n)
+{
+ register WINDOW *wp1;
+ register WINDOW *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;
+}
+#endif
+
+/*
+ * 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
+ * distruction of a window makes a buffer
+ * become undisplayed.
+ */
+/*ARGSUSED*/
+onlywind(f, n)
+{
+ register WINDOW *wp;
+ register LINE *lp;
+ register 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;
+ }
+ free((char *) 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;
+ }
+ free((char *) wp);
+ }
+ lp = curwp->w_linep;
+ i = curwp->w_toprow;
+ while (i!=0 && lback(lp)!=curbp->b_linep) {
+ --i;
+ lp = lback(lp);
+ }
+ curwp->w_toprow = 0;
+ curwp->w_ntrows = nrow-2; /* 2 = mode, echo. */
+ curwp->w_linep = lp;
+ curwp->w_flag |= WFMODE|WFHARD;
+ 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.
+ */
+/*ARGSUSED*/
+splitwind(f, n)
+{
+ register WINDOW *wp;
+ register LINE *lp;
+ register int ntru;
+ register int ntrd;
+ int ntrl;
+ WINDOW *wp1, *wp2;
+
+ if (curwp->w_ntrows < 3) {
+ ewprintf("Cannot split a %d line window", curwp->w_ntrows);
+ return (FALSE);
+ }
+ if ((wp = (WINDOW *)malloc(sizeof(WINDOW))) == NULL) {
+ ewprintf("Can't get %d", sizeof(WINDOW));
+ return (FALSE);
+ }
+ ++curbp->b_nwnd; /* Displayed twice. */
+ wp->w_bufp = curbp;
+ 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_flag = 0;
+ wp->w_force = 0;
+ ntru = (curwp->w_ntrows-1) / 2; /* Upper size */
+ ntrl = (curwp->w_ntrows-1) - ntru; /* Lower size */
+ lp = curwp->w_linep;
+ ntrd = 0;
+ while (lp != curwp->w_dotp) {
+ ++ntrd;
+ lp = lforw(lp);
+ }
+ lp = curwp->w_linep;
+ if (ntrd <= ntru) { /* Old is upper window. */
+ if (ntrd == ntru) /* Hit mode line. */
+ 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;
+ } else { /* Old is lower window */
+ 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;
+ ++ntru; /* Mode line. */
+ curwp->w_toprow += ntru;
+ curwp->w_ntrows = ntrl;
+ while (ntru--)
+ lp = lforw(lp);
+ }
+ curwp->w_linep = lp; /* Adjust the top lines */
+ wp->w_linep = lp; /* if necessary. */
+ curwp->w_flag |= WFMODE|WFHARD;
+ wp->w_flag |= WFMODE|WFHARD;
+ 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*/
+enlargewind(f, n)
+{
+ register WINDOW *adjwp;
+ register LINE *lp;
+ register int i;
+
+ if (n < 0)
+ return shrinkwind(f, -n);
+ if (wheadp->w_wndp == NULL) {
+ 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) {
+ ewprintf("Impossible change");
+ return FALSE;
+ }
+ if (curwp->w_wndp == adjwp) { /* Shrink below. */
+ lp = adjwp->w_linep;
+ for (i=0; i<n && lp!=adjwp->w_bufp->b_linep; ++i)
+ lp = lforw(lp);
+ adjwp->w_linep = lp;
+ adjwp->w_toprow += n;
+ } else { /* Shrink above. */
+ lp = curwp->w_linep;
+ for (i=0; i<n && lback(lp)!=curbp->b_linep; ++i)
+ lp = lback(lp);
+ curwp->w_linep = lp;
+ curwp->w_toprow -= n;
+ }
+ curwp->w_ntrows += n;
+ adjwp->w_ntrows -= n;
+ curwp->w_flag |= WFMODE|WFHARD;
+ adjwp->w_flag |= WFMODE|WFHARD;
+ 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.
+ */
+shrinkwind(f, n)
+{
+ register WINDOW *adjwp;
+ register LINE *lp;
+ register int i;
+
+ if (n < 0)
+ return enlargewind(f, -n);
+ if (wheadp->w_wndp == NULL) {
+ ewprintf("Only one window");
+ return FALSE;
+ }
+ /*
+ * Bit of flakiness - KRANDOM means it was an internal call, and
+ * to be trusted implicitly about sizes.
+ */
+ if ( !(f & FFRAND) && curwp->w_ntrows <= n) {
+ ewprintf("Impossible change");
+ return (FALSE);
+ }
+ if ((adjwp=curwp->w_wndp) == NULL) {
+ adjwp = wheadp;
+ while (adjwp->w_wndp != curwp)
+ adjwp = adjwp->w_wndp;
+ }
+ if (curwp->w_wndp == adjwp) { /* Grow below. */
+ lp = adjwp->w_linep;
+ for (i=0; i<n && lback(lp)!=adjwp->w_bufp->b_linep; ++i)
+ lp = lback(lp);
+ adjwp->w_linep = lp;
+ adjwp->w_toprow -= n;
+ } else { /* Grow above. */
+ lp = curwp->w_linep;
+ for (i=0; i<n && lp!=curbp->b_linep; ++i)
+ lp = lforw(lp);
+ curwp->w_linep = lp;
+ curwp->w_toprow += n;
+ }
+ curwp->w_ntrows -= n;
+ adjwp->w_ntrows += n;
+ curwp->w_flag |= WFMODE|WFHARD;
+ adjwp->w_flag |= WFMODE|WFHARD;
+ return (TRUE);
+}
+
+/*
+ * Delete current window. Call shrink-window to do the screen
+ * updating, then throw away the window.
+ */
+/*ARGSUSED*/
+delwind(f, n)
+{
+ register WINDOW *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;
+ }
+ /* 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((char *) wp);
+ return TRUE;
+}
+/*
+ * Pick a window for a pop-up.
+ * Split the screen if there is only
+ * one window. Pick the uppermost window that
+ * isn't the current window. An LRU algorithm
+ * might be better. Return a pointer, or
+ * NULL on error.
+ */
+WINDOW *
+wpopup() {
+ register WINDOW *wp;
+
+ if (wheadp->w_wndp == NULL
+ && splitwind(FFRAND, 0) == FALSE)
+ return NULL;
+ wp = wheadp; /* Find window to use */
+ while (wp!=NULL && wp==curwp)
+ wp = wp->w_wndp;
+ return wp;
+}
diff --git a/usr.bin/mg/word.c b/usr.bin/mg/word.c
new file mode 100644
index 00000000000..e06b4cab2a8
--- /dev/null
+++ b/usr.bin/mg/word.c
@@ -0,0 +1,252 @@
+/*
+ * 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 "def.h"
+
+/*
+ * Move the cursor backward by
+ * "n" words. All of the details of motion
+ * are performed by the "backchar" and "forwchar"
+ * routines.
+ */
+/*ARGSUSED*/
+backword(f, 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*/
+forwword(f, 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;
+}
+
+/*
+ * Move the cursor forward by
+ * the specified number of words. As you move,
+ * convert any characters to upper case.
+ */
+/*ARGSUSED*/
+upperword(f, n)
+{
+ register int c;
+
+ if (n < 0) return FALSE;
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ return TRUE;
+ }
+ 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(WFHARD);
+ }
+ 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*/
+lowerword(f, n)
+{
+ register int c;
+
+ if (n < 0) return FALSE;
+ while (n--) {
+ while (inword() == FALSE) {
+ 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(WFHARD);
+ }
+ 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 and move past the end of the buffer.
+ */
+/*ARGSUSED*/
+capword(f, n)
+{
+ register int c;
+ VOID lchange();
+
+ if (n < 0) return FALSE;
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ return TRUE;
+ }
+ 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(WFHARD);
+ }
+ 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(WFHARD);
+ }
+ if (forwchar(FFRAND, 1) == FALSE)
+ return TRUE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * Kill forward by "n" words.
+ */
+/*ARGSUSED*/
+delfword(f, n)
+{
+ register RSIZE size;
+ register LINE *dotp;
+ register int doto;
+
+ if (n < 0)
+ return FALSE;
+ if ((lastflag&CFKILL) == 0) /* Purge kill buffer. */
+ kdelete();
+ thisflag |= CFKILL;
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+ size = 0;
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ goto out; /* Hit end of buffer. */
+ ++size;
+ }
+ while (inword() != FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ goto out; /* Hit end of buffer. */
+ ++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 wierd. Normally this is bound to "M-Rubout" and
+ * to "M-Backspace".
+ */
+/*ARGSUSED*/
+delbword(f, n)
+{
+ register RSIZE size;
+ VOID kdelete();
+
+ if (n < 0) return FALSE;
+ if ((lastflag&CFKILL) == 0) /* Purge kill buffer. */
+ kdelete();
+ thisflag |= CFKILL;
+ if (backchar(FFRAND, 1) == FALSE)
+ return (TRUE); /* Hit buffer start. */
+ size = 1; /* One deleted. */
+ while (n--) {
+ while (inword() == FALSE) {
+ if (backchar(FFRAND, 1) == FALSE)
+ goto out; /* Hit buffer start. */
+ ++size;
+ }
+ while (inword() != FALSE) {
+ if (backchar(FFRAND, 1) == FALSE)
+ goto out; /* Hit buffer start. */
+ ++size;
+ }
+ }
+ if (forwchar(FFRAND, 1) == FALSE)
+ return FALSE;
+ --size; /* Undo assumed delete. */
+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 setable.
+ */
+inword() {
+/* 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]);
+}
+