BSD 4_3_Reno development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Sun, 7 May 1989 07:31:00 +0000 (23:31 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Sun, 7 May 1989 07:31:00 +0000 (23:31 -0800)
Work on file usr/src/contrib/jove/macros.c
Work on file usr/src/contrib/jove/insert.c

Synthesized-from: CSRG/cd2/4.3reno

usr/src/contrib/jove/insert.c [new file with mode: 0644]
usr/src/contrib/jove/macros.c [new file with mode: 0644]

diff --git a/usr/src/contrib/jove/insert.c b/usr/src/contrib/jove/insert.c
new file mode 100644 (file)
index 0000000..b1f61a9
--- /dev/null
@@ -0,0 +1,776 @@
+/***************************************************************************
+ * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
+ * is provided to you without charge, and with no warranty.  You may give  *
+ * away copies of JOVE, including sources, provided that this notice is    *
+ * included in all the files.                                              *
+ ***************************************************************************/
+
+#include "jove.h"
+#include "ctype.h"
+#include "list.h"
+#include "chars.h"
+#include "disp.h"
+
+private int
+       newchunk proto((void));
+private void
+       DoNewline proto((int indentp)),
+       init_specials proto((void));
+
+/* Make a newline after AFTER in buffer BUF, UNLESS after is 0,
+   in which case we insert the newline before after. */
+
+Line *
+listput(buf, after)
+register Buffer        *buf;
+register Line  *after;
+{
+       register Line   *newline = nbufline();
+
+       if (after == 0) {       /* Before the first line */
+               newline->l_next = buf->b_first;
+               newline->l_prev = 0;
+               buf->b_first = newline;
+       } else {
+               newline->l_prev = after;
+               newline->l_next = after->l_next;
+               after->l_next = newline;
+       }
+       if (newline->l_next)
+               newline->l_next->l_prev = newline;
+       else
+               if (buf)
+                       buf->b_last = newline;
+       if (buf && buf->b_dot == 0)
+               buf->b_dot = newline;
+       return newline;
+}
+
+/* Divide the current line and move the current line to the next one */
+
+void
+LineInsert(num)
+register int   num;
+{
+       char    newline[LBSIZE];
+       register Line   *newdot,
+                       *olddot;
+       int     oldchar;
+
+       olddot = curline;
+       oldchar = curchar;
+
+       newdot = curline;
+       while (--num >= 0) {
+               newdot = listput(curbuf, newdot);
+               SavLine(newdot, NullStr);
+       }
+
+       modify();
+       if (curchar != 0) {
+               strcpy(newline, &linebuf[curchar]);
+               linebuf[curchar] = '\0';        /* Shorten this line */
+               SavLine(curline, linebuf);
+               strcpy(linebuf, newline);
+       } else {        /* Redisplay optimization */
+               newdot->l_dline = curline->l_dline;
+               SavLine(curline, NullStr);
+       }
+
+       makedirty(curline);
+       curline = newdot;
+       curchar = 0;
+       makedirty(curline);
+       IFixMarks(olddot, oldchar, curline, curchar);
+}
+
+/* Inserts tabs and spaces to move the cursor to column GOAL.  It
+   Uses the most optimal number of tabs and spaces no matter what
+   was there before hand. */
+
+void
+n_indent(goal)
+register int   goal;
+{
+       int     dotcol,
+               incrmt;
+
+       DelWtSpace();
+       dotcol = calc_pos(linebuf, curchar);
+
+       for (;;) {
+               incrmt = (tabstop - (dotcol % tabstop));
+               if (dotcol + incrmt > goal)
+                       break;
+               insert_c('\t', 1);
+               dotcol += incrmt;
+       }
+       if (dotcol != goal)
+               insert_c(' ', (goal - dotcol));
+}
+
+#ifdef ABBREV
+void
+MaybeAbbrevExpand()
+{
+       if (MinorMode(Abbrev) && !ismword(LastKeyStruck) &&
+           !bolp() && ismword(linebuf[curchar - 1]))
+               AbbrevExpand();
+}
+#endif
+
+void
+SelfInsert()
+{
+#ifdef ABBREV
+       MaybeAbbrevExpand();
+#endif
+       if (LastKeyStruck != CTL('J') && MinorMode(OverWrite)) {
+               register int    num,
+                               i;
+
+               for (i = 0, num = arg_value(); i < num; i++) {
+                       int     pos = calc_pos(linebuf, curchar);
+
+                       if (!eolp()) {
+                               if (linebuf[curchar] == '\t') {
+                                       if ((pos + 1) == ((pos + tabstop) - (pos % tabstop)))
+                                               del_char(FORWARD, 1, NO);
+                               } else
+                                       del_char(FORWARD, 1, NO);
+                       }
+                       insert_c(LastKeyStruck, 1);
+               }
+       } else
+               Insert(LastKeyStruck);
+
+       if (MinorMode(Fill) && (curchar >= RMargin ||
+                              (calc_pos(linebuf, curchar) >= RMargin))) {
+               int margin;
+               Bufpos save;
+
+               if (MinorMode(Indent)) {
+                       DOTsave(&save);
+                       ToIndent();
+                       margin = calc_pos(linebuf, curchar);
+                       SetDot(&save);
+               } else
+                       margin = LMargin;
+               DoJustify(curline, 0, curline,
+                         curchar + (int)strlen(&linebuf[curchar]), 1, margin);
+       }
+}
+
+void
+Insert(c)
+int    c;
+{
+       if (c == CTL('J'))
+               LineInsert(arg_value());
+       else
+               insert_c(c, arg_value());
+}
+
+/* insert character C N times at point */
+void
+insert_c(c, n)
+int    c,
+       n;
+{
+       if (n <= 0)
+               return;
+       modify();
+       makedirty(curline);
+       ins_c(c, linebuf, curchar, n, LBSIZE);
+       IFixMarks(curline, curchar, curline, curchar + n);
+       curchar += n;
+}
+
+/* Tab in to the right place for C mode */
+
+void
+Tab()
+{
+#ifdef LISP
+       if (MajorMode(LISPMODE) && (bolp() || !eolp())) {
+               int     dotchar = curchar;
+               Mark    *m = 0;
+
+               ToIndent();
+               if (dotchar > curchar)
+                       m = MakeMark(curline, dotchar, M_FLOATER);
+               (void) lisp_indent();
+               if (m) {
+                       ToMark(m);
+                       DelMark(m);
+               } else
+                       ToIndent();
+               return;
+       }
+#endif
+       if (MajorMode(CMODE)) {
+               if (within_indent())
+                       (void) c_indent(NO);
+               else {
+                       int     curpos,
+                               tabbed_pos;
+
+                       skip_wht_space();
+                       curpos = calc_pos(linebuf, curchar);
+                       tabbed_pos = curpos + (CIndIncrmt - (curpos % CIndIncrmt));
+                       n_indent(tabbed_pos);
+               }
+       } else
+               SelfInsert();
+}
+
+void
+QuotChar()
+{
+       int     c,
+               slow = NO;
+
+       c = waitchar(&slow);
+       if (c != CTL('@'))
+               Insert(c);
+}
+
+/* Insert the paren.  If in C mode and c is a '}' then insert the
+   '}' in the "right" place for C indentation; that is indented
+   the same amount as the matching '{' is indented. */
+
+int    PDelay = 5,     /* 1/2 a second */
+       CIndIncrmt = 8;
+
+void
+DoParen()
+{
+       Bufpos  *bp;
+       int     tried = NO,
+               nx,
+               c = LastKeyStruck;
+
+       if (!isclosep(c)) {
+               SelfInsert();
+               return;
+       }
+
+       if (MajorMode(CMODE) && c == '}' && within_indent()) {
+               bp = c_indent(YES);
+               tried = TRUE;
+       }
+#ifdef LISP
+       if (MajorMode(LISPMODE) && c == ')' && blnkp(linebuf)) {
+               bp = lisp_indent();
+               tried = TRUE;
+       }
+#endif
+       SelfInsert();
+#ifdef MAC
+       if (MinorMode(ShowMatch) && !in_macro()) {
+#else
+       if (MinorMode(ShowMatch) && !charp() && !in_macro()) {
+#endif
+               b_char(1);      /* Back onto the ')' */
+               if (!tried)
+                       bp = m_paren(c, BACKWARD, NO, YES);
+               f_char(1);
+               if (bp != 0) {
+                       nx = in_window(curwind, bp->p_line);
+                       if (nx != -1) {         /* is visible */
+                               Bufpos  b;
+
+                               DOTsave(&b);
+                               SetDot(bp);
+                               SitFor(PDelay);
+                               SetDot(&b);
+                       } else
+                               s_mess("%s", lcontents(bp->p_line));
+               }
+               mp_error();     /* display error message */
+       }
+}
+
+void
+LineAI()
+{
+       DoNewline(TRUE);
+}
+
+void
+Newline()
+{
+       DoNewline(MinorMode(Indent));
+}
+
+private void
+DoNewline(indentp)
+int    indentp;
+{
+       Bufpos  save;
+       int     indent;
+
+       /* first we calculate the indent of the current line */
+       DOTsave(&save);
+       ToIndent();
+       indent = calc_pos(linebuf, curchar);
+       SetDot(&save);
+
+#ifdef ABBREV
+       MaybeAbbrevExpand();
+#endif
+#ifdef LISP
+       if (MajorMode(LISPMODE))
+               DelWtSpace();
+       else
+#endif
+           if (indentp || blnkp(linebuf))
+               DelWtSpace();
+
+       /* If there is more than 2 blank lines in a row then don't make
+          a newline, just move down one. */
+       if (arg_value() == 1 && eolp() && TwoBlank())
+               SetLine(curline->l_next);
+       else
+               LineInsert(arg_value());
+
+       if (indentp)
+#ifdef LISP
+           if (MajorMode(LISPMODE))
+               (void) lisp_indent();
+           else
+#endif
+           {
+               Bol();
+               n_indent((LMargin == 0) ? indent : LMargin);
+           }
+}
+
+void
+ins_str(str, ok_nl)
+register char  *str;
+int    ok_nl;
+{
+       register char   c;
+       Bufpos  save;
+       int     llen;
+
+       if (*str == 0)
+               return;         /* ain't nothing to insert! */
+       DOTsave(&save);
+       llen = strlen(linebuf);
+       while ((c = *str++) != '\0') {
+               if (c == '\n' || (ok_nl && llen >= LBSIZE - 2)) {
+                       IFixMarks(save.p_line, save.p_char, curline, curchar);
+                       modify();
+                       makedirty(curline);
+                       LineInsert(1);
+                       DOTsave(&save);
+                       llen = strlen(linebuf);
+               }
+               if (c != '\n') {
+                       ins_c(c, linebuf, curchar++, 1, LBSIZE);
+                       llen += 1;
+               }
+       }
+       IFixMarks(save.p_line, save.p_char, curline, curchar);
+       modify();
+       makedirty(curline);
+}
+
+void
+open_lines(n)
+int    n;
+{
+       Bufpos  dot;
+
+       DOTsave(&dot);
+       LineInsert(n);  /* Open the lines... */
+       SetDot(&dot);
+}
+
+void
+OpenLine()
+{
+       open_lines(arg_value());
+}
+
+/* Take the region FLINE/FCHAR to TLINE/TCHAR and insert it at
+   ATLINE/ATCHAR in WHATBUF. */
+
+Bufpos *
+DoYank(fline, fchar, tline, tchar, atline, atchar, whatbuf)
+Line   *fline,
+       *tline,
+       *atline;
+int    fchar,
+       tchar,
+       atchar;
+Buffer *whatbuf;
+{
+       register Line   *newline;
+       static Bufpos   bp;
+       char    save[LBSIZE],
+               buf[LBSIZE];
+       Line    *startline = atline;
+       int     startchar = atchar;
+
+       lsave();
+       if (whatbuf)
+               modify();
+       (void) ltobuf(atline, genbuf);
+       strcpy(save, &genbuf[atchar]);
+
+       (void) ltobuf(fline, buf);
+       if (fline == tline)
+               buf[tchar] = '\0';
+
+       linecopy(genbuf, atchar, &buf[fchar]);
+       atline->l_dline = putline(genbuf);
+       makedirty(atline);
+
+       fline = fline->l_next;
+       while (fline != tline->l_next) {
+               newline = listput(whatbuf, atline);
+               newline->l_dline = fline->l_dline;
+               makedirty(newline);
+               fline = fline->l_next;
+               atline = newline;
+               atchar = 0;
+       }
+
+       getline(atline->l_dline, genbuf);
+       atchar += tchar;
+       linecopy(genbuf, atchar, save);
+       atline->l_dline = putline(genbuf);
+       makedirty(atline);
+       IFixMarks(startline, startchar, atline, atchar);
+       bp.p_line = atline;
+       bp.p_char = atchar;
+       this_cmd = YANKCMD;
+       getDOT();                       /* Whatever used to be in linebuf */
+       return &bp;
+}
+
+void
+YankPop()
+{
+       Line    *line,
+               *last;
+       Mark    *mp = CurMark();
+       Bufpos  *dot;
+       int     dir = -1;       /* Direction to rotate the ring */
+
+       if (last_cmd != YANKCMD)
+               complain("Yank something first!");
+
+       lfreelist(reg_delete(mp->m_line, mp->m_char, curline, curchar));
+
+       /* Now must find a recently killed region. */
+
+       if (arg_value() < 0)
+               dir = 1;
+
+       killptr += dir;
+       for (;;) {
+               if (killptr < 0)
+                       killptr = NUMKILLS - 1;
+               else if (killptr >= NUMKILLS)
+                       killptr = 0;
+               if (killbuf[killptr])
+                       break;
+               killptr += dir;
+       }
+
+       this_cmd = YANKCMD;
+
+       line = killbuf[killptr];
+       last = lastline(line);
+       dot = DoYank(line, 0, last, length(last), curline, curchar, curbuf);
+       MarkSet(CurMark(), curline, curchar);
+       SetDot(dot);
+}
+
+/* This is an attempt to reduce the amount of memory taken up by each line.
+   Without this each malloc of a line uses sizeof (line) + sizeof(HEADER)
+   where line is 3 words and HEADER is 1 word.
+   This is going to allocate memory in chucks of CHUNKSIZE * sizeof (line)
+   and divide each chuck into lineS.  A line is free in a chunk when its
+   line->l_dline == 0, so freeline sets dline to 0. */
+
+#define CHUNKSIZE      300
+
+struct chunk {
+       int     c_nlines;       /* Number of lines in this chunk (so they
+                                  don't all have to be CHUNKSIZE long). */
+       Line    *c_block;       /* Chunk of memory */
+       struct chunk    *c_nextfree;    /* Next chunk of lines */
+};
+
+private struct chunk   *fchunk = 0;
+private Line   *ffline = 0;    /* First free line */
+
+void
+freeline(line)
+register Line  *line;
+{
+       line->l_dline = 0;
+       line->l_next = ffline;
+       if (ffline)
+               ffline->l_prev = line;
+       line->l_prev = 0;
+       ffline = line;
+}
+
+void
+lfreelist(first)
+register Line  *first;
+{
+       if (first)
+               lfreereg(first, lastline(first));
+}
+
+/* Append region from line1 to line2 onto the free list of lines */
+
+void
+lfreereg(line1, line2)
+register Line  *line1,
+               *line2;
+{
+       register Line   *next,
+                       *last = line2->l_next;
+
+       while (line1 != last) {
+               next = line1->l_next;
+               freeline(line1);
+               line1 = next;
+       }
+}
+
+private int
+newchunk()
+{
+       register Line   *newline;
+       register int    i;
+       struct chunk    *f;
+       int     nlines = CHUNKSIZE;
+
+       f = (struct chunk *) emalloc(sizeof (struct chunk));
+       if (f == 0)
+               return 0;
+
+       if ((f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines))) == 0) {
+               while (nlines > 0) {
+                       f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines));
+                       if (f->c_block != 0)
+                               break;
+                       nlines /= 2;
+               }
+       }
+
+       if (nlines <= 0)
+               return 0;
+
+       f->c_nlines = nlines;
+       for (i = 0, newline = f->c_block; i < nlines; newline++, i++)
+               freeline(newline);
+       f->c_nextfree = fchunk;
+       fchunk = f;
+       return 1;
+}
+
+/* New BUFfer LINE */
+
+Line *
+nbufline()
+{
+       register Line   *newline;
+
+       if (ffline == 0)        /* No free list */
+               if (newchunk() == 0)
+                       complain("[Out of lines] ");
+       newline = ffline;
+       ffline = ffline->l_next;
+       if (ffline)
+               ffline->l_prev = 0;
+       return newline;
+}
+
+/* Remove the free lines, in chunk c, from the free list because they are
+   no longer free. */
+
+private void
+remfreelines(c)
+register struct chunk  *c;
+{
+       register Line   *lp;
+       register int    i;
+
+       for (lp = c->c_block, i = 0; i < c->c_nlines; i++, lp++) {
+               if (lp->l_prev)
+                       lp->l_prev->l_next = lp->l_next;
+               else
+                       ffline = lp->l_next;
+               if (lp->l_next)
+                       lp->l_next->l_prev = lp->l_prev;
+       }
+}
+
+/* This is used to garbage collect the chunks of lines when malloc fails
+   and we are NOT looking for a new buffer line.  This goes through each
+   chunk, and if every line in a given chunk is not allocated, the entire
+   chunk is `free'd by "free()". */
+
+void
+GCchunks()
+{
+       register struct chunk   *cp;
+       struct chunk    *prev = 0,
+                       *next = 0;
+       register int    i;
+       register Line   *newline;
+
+       for (cp = fchunk; cp != 0; cp = next) {
+               for (i = 0, newline = cp->c_block; i < cp->c_nlines; newline++, i++)
+                       if (newline->l_dline != 0)
+                               break;
+
+               next = cp->c_nextfree;
+
+               if (i == cp->c_nlines) {                /* Unlink it!!! */
+                       if (prev)
+                               prev->c_nextfree = cp->c_nextfree;
+                       else
+                               fchunk = cp->c_nextfree;
+                       remfreelines(cp);
+                       free((char *) cp->c_block);
+                       free((char *) cp);
+               } else
+                       prev = cp;
+       }
+}
+
+#ifdef LISP
+
+#include "re.h"
+
+/* Grind S-Expr */
+
+void
+GSexpr()
+{
+       Bufpos  dot,
+               end;
+
+       if (linebuf[curchar] != '(')
+               complain((char *) 0);
+       DOTsave(&dot);
+       FSexpr();
+       DOTsave(&end);
+       SetDot(&dot);
+       for (;;) {
+               if (curline == end.p_line)
+                       break;
+               line_move(FORWARD, 1, NO);
+               if (!blnkp(linebuf))
+                       (void) lisp_indent();
+       }
+       SetDot(&dot);
+}
+
+/* lisp_indent() indents a new line in Lisp Mode, according to where
+   the matching close-paren would go if we typed that (sort of). */
+
+private List   *specials = NIL;
+
+private void
+init_specials()
+{
+       static char *const words[] = {
+               "case",
+               "def",
+               "dolist",
+               "fluid-let",
+               "lambda",
+               "let",
+               "lexpr",
+               "macro",
+               "named-l",      /* named-let and named-lambda */
+               "nlambda",
+               "prog",
+               "selectq",
+               0
+       };
+       char    *const *wordp = words;
+
+       while (*wordp)
+               list_push(&specials, (Element *) *wordp++);
+}
+
+void
+AddSpecial()
+{
+       char    *word;
+       register List   *lp;
+
+       if (specials == NIL)
+               init_specials();
+       word = ask((char *) 0, ProcFmt);
+       for (lp = specials; lp != NIL; lp = list_next(lp))
+               if (strcmp((char *) list_data(lp), word) == 0)
+                       return;         /* already in list */
+       (void) list_push(&specials, (Element *) copystr(word));
+}
+
+Bufpos *
+lisp_indent()
+{
+       Bufpos  *bp,
+               savedot;
+       int     goal;
+
+       bp = m_paren(')', BACKWARD, NO, YES);
+
+       if (bp == NULL)
+               return NULL;
+
+       /* We want to end up
+
+               (atom atom atom ...
+                     ^ here.
+        */
+
+       DOTsave(&savedot);
+       SetDot(bp);
+       f_char(1);
+       if (linebuf[curchar] != '(') {
+               register List   *lp;
+
+               if (specials == NIL)
+                       init_specials();
+               for (lp = specials; lp != NIL; lp = list_next(lp))
+                       if (casencmp((char *) list_data(lp),
+                                    &linebuf[curchar],
+                                    strlen((char *) list_data(lp))) == 0)
+                               break;
+               if (lp == NIL) {        /* not special */
+                       int     c_char = curchar;
+
+                       WITH_TABLE(curbuf->b_major)
+                               f_word(1);
+                       END_TABLE();
+                       if (LookingAt("[ \t]*;\\|[ \t]*$", linebuf, curchar))
+                               curchar = c_char;
+                       else while (linebuf[curchar] == ' ')
+                               curchar += 1;
+               } else
+                       curchar += 1;
+       }
+       goal = calc_pos(linebuf, curchar);
+       SetDot(&savedot);
+       Bol();
+       n_indent(goal);
+
+       return bp;
+}
+#endif /* LISP */
diff --git a/usr/src/contrib/jove/macros.c b/usr/src/contrib/jove/macros.c
new file mode 100644 (file)
index 0000000..0f9f564
--- /dev/null
@@ -0,0 +1,408 @@
+/***************************************************************************
+ * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
+ * is provided to you without charge, and with no warranty.  You may give  *
+ * away copies of JOVE, including sources, provided that this notice is    *
+ * included in all the files.                                              *
+ ***************************************************************************/
+
+#include "jove.h"
+#include "ctype.h"
+#include "fp.h"
+#include "chars.h"
+#include "disp.h"
+
+private void
+       add_mac proto((struct macro *)),
+       del_mac proto((struct macro *)),
+       pop_macro_stack proto((void)),
+       push_macro_stack proto((struct macro *, int));
+
+private struct macro *mac_exists proto((char *));
+
+#define SAVE           01      /* this macro needs saving to a file */
+
+struct macro   *macros = 0;            /* macros */
+int    InMacDefine = NO;
+
+private void
+add_mac(new)
+struct macro   *new;
+{
+       register struct macro   *mp,
+                               *prev = 0;
+
+       for (mp = macros; mp != 0; prev = mp, mp = mp->m_nextm)
+               if (mp == new)
+                       return;
+
+       if (prev)
+               prev->m_nextm = new;
+       else
+               macros = new;
+       new->m_nextm = 0;
+       new->Type = MACRO;
+}
+
+private void
+del_mac(mac)
+struct macro   *mac;
+{
+       register struct macro   *m;
+
+       for (m = macros; m != 0; m = m->m_nextm)
+               if (m->m_nextm == mac) {
+                       m->m_nextm = mac->m_nextm;
+                       break;
+               }
+       free(mac->Name);
+       free(mac->m_body);
+       free((char *) mac);
+}
+
+struct macro   KeyMacro;       /* Macro used for defining */
+
+/* To execute a macro, we have a "stack" of running macros.  Whenever
+   we execute a macro, we push it on the stack, run it, then pop it
+   from the stack.  */
+struct m_thread {
+       struct m_thread *mt_prev;
+       struct macro    *mt_mp;
+       int     mt_offset,
+               mt_count;
+};
+
+private struct m_thread        *mac_stack = 0;
+
+private struct m_thread *
+alloc_mthread()
+{
+       return (struct m_thread *) emalloc(sizeof (struct m_thread));
+}
+
+private void
+free_mthread(t)
+struct m_thread        *t;
+{
+       free((char *) t);
+}
+
+void
+unwind_macro_stack()
+{
+       while (mac_stack != 0)
+               pop_macro_stack();
+}
+
+private void
+pop_macro_stack()
+{
+       register struct m_thread        *m;
+
+       if ((m = mac_stack) == 0)
+               return;
+       mac_stack = m->mt_prev;
+       free_mthread(m);
+}
+
+private void
+push_macro_stack(m, count)
+register struct macro  *m;
+int    count;
+{
+       register struct m_thread        *t;
+
+       for (t = mac_stack; t != 0; t = t->mt_prev)
+               if (t->mt_mp == m)
+                       complain("[Cannot execute macro recusively]");
+       if (count <= 0)
+               complain("[Cannot execute macro a negative number of times]");
+       t = alloc_mthread();
+       t->mt_prev = mac_stack;
+       mac_stack = t;
+       t->mt_offset = 0;
+       t->mt_mp = m;
+       t->mt_count = count;
+}
+
+void
+do_macro(mac)
+struct macro   *mac;
+{
+       push_macro_stack(mac, arg_value());
+}
+
+private struct macro *
+mac_exists(name)
+char   *name;
+{
+       register struct macro   *mp;
+
+       for (mp = macros; mp; mp = mp->m_nextm)
+               if (strcmp(mp->Name, name) == 0)
+                       return mp;
+       return 0;
+}
+
+void
+mac_init()
+{
+       add_mac(&KeyMacro);
+       KeyMacro.Name = "keyboard-macro";
+       KeyMacro.m_len = 0;
+       KeyMacro.m_buflen = 16;
+       KeyMacro.m_body = emalloc((size_t) KeyMacro.m_buflen);
+}
+
+void
+mac_putc(c)
+int    c;
+{
+       if (KeyMacro.m_len >= KeyMacro.m_buflen) {
+               KeyMacro.m_buflen += 16;
+               KeyMacro.m_body = realloc(KeyMacro.m_body, (size_t) KeyMacro.m_buflen);
+               if (KeyMacro.m_body == 0) {
+                       KeyMacro.m_buflen = KeyMacro.m_len = 0;
+                       complain("[Can't allocate storage for keyboard macro]");
+               }
+       }
+       KeyMacro.m_body[KeyMacro.m_len++] = c;
+}
+
+int
+in_macro()
+{
+       return (mac_stack != NULL);
+}
+
+int
+mac_getc()
+{
+       struct m_thread *mthread;
+       struct macro    *m;
+
+       if ((mthread = mac_stack) == NULL)
+               return EOF;
+       m = mthread->mt_mp;
+       if (mthread->mt_offset == m->m_len) {
+               mthread->mt_offset = 0;
+               if (--mthread->mt_count == 0)
+                       pop_macro_stack();
+               return mac_getc();
+       }
+       return m->m_body[mthread->mt_offset++];
+}
+
+void
+NameMac()
+{
+       char    *name;
+       struct macro    *m;
+
+       if (KeyMacro.m_len == 0)
+               complain("[No keyboard macro to name!]");
+       if (in_macro() || InMacDefine)
+               complain("[Can't name while defining/executing]");
+       if ((m = mac_exists(name = ask((char *) 0, ProcFmt))) == 0)
+               m = (struct macro *) emalloc(sizeof *m);
+       else {
+               if (strcmp(name, KeyMacro.Name) == 0)
+                       complain("[Can't name it that!]");
+               free(m->Name);
+               free(m->m_body);
+       }
+       name = copystr(name);
+       m->Type = KeyMacro.Type;
+       m->m_len = KeyMacro.m_len;
+       m->m_buflen = KeyMacro.m_buflen;
+       m->m_body = emalloc((size_t) m->m_buflen);
+       byte_copy(KeyMacro.m_body, m->m_body, (size_t) m->m_len);
+       m->m_flags = SAVE;
+       m->Name = name;
+       add_mac(m);
+}
+
+void
+RunMacro()
+{
+       struct macro    *m;
+
+       if ((m = (struct macro *) findmac(ProcFmt)) != NULL)
+               do_macro(m);
+}
+
+private void
+pr_putc(c, fp)
+int    c;
+File   *fp;
+{
+       if (c == '\\' || c == '^')
+               jputc('\\', fp);
+        else if (isctrl(c)) {
+               jputc('^', fp);
+               c = (c == RUBOUT) ? '?' : (c + '@');
+       }
+       jputc(c, fp);
+}
+
+void
+WriteMacs()
+{
+       struct macro    *m;
+       char    *file,
+               filebuf[FILESIZE];
+       File    *fp;
+       int     i;
+
+       file = ask_file((char *) 0, (char *) 0, filebuf);
+       fp = open_file(file, iobuff, F_WRITE, YES, YES);
+
+       /* Don't write the keyboard macro which is always the first */
+       for (m = macros->m_nextm; m != 0; m = m->m_nextm) {
+               fwritef(fp, "define-macro %s ", m->Name);
+               for (i = 0; i < m->m_len; i++)
+                       pr_putc(m->m_body[i], fp);
+               jputc('\n', fp);
+               m->m_flags &= ~SAVE;
+       }
+       close_file(fp);
+}
+
+void
+DefKBDMac()
+{
+       char    *macro_name,
+               *macro_body,
+               nextc,
+               c,
+               macro_buffer[LBSIZE];
+       int     i;
+       struct macro    *m;
+
+       macro_name = do_ask(" \r\n", (int (*) proto((int))) 0, (char *) 0,
+               ProcFmt);
+       if (macro_name == 0)
+               complain("[No default]");
+       macro_name = copystr(macro_name);
+       if ((m = mac_exists(macro_name)) != NULL)
+               del_mac(m);
+       macro_body = ask((char *) 0, ": %f %s enter body: ", macro_name);
+       i = 0;
+       while ((c = *macro_body++) != '\0') {
+               if (c == '\\') {
+                       if ((nextc = *macro_body++) == LF)
+                               complain("[Premature end of line]");
+                       c = nextc;
+               } else if (c == '^') {
+                       if ((nextc = *macro_body++) == '?')
+                               c = RUBOUT;
+                       else if (isalpha(nextc) || strchr("@[\\]^_", nextc))
+                               c = CTL(nextc);
+                       else
+                               complain("Bad control-character: '%c'", nextc);
+               }
+               macro_buffer[i++] = c;
+       }
+       m = (struct macro *) emalloc(sizeof (*m));
+       m->Name = macro_name;
+       m->m_len = m->m_buflen = i;
+       m->m_body = emalloc((size_t) i);
+       m->m_flags = InJoverc ? 0 : SAVE;
+       byte_copy(macro_buffer, m->m_body, (size_t) i);
+       add_mac(m);
+}
+
+void
+Remember()
+{
+       /* We're already executing the macro; ignore any attempts
+          to define the keyboard macro while we are executing. */
+       if (in_macro())
+               return;
+       if (InMacDefine)
+               message("[Already defining ... continue with definition]");
+       else {
+               UpdModLine = YES;
+               InMacDefine = YES;
+               KeyMacro.m_len = 0;
+               message("Defining...");
+       }
+}
+
+void
+Forget()
+{
+       char    *cp;
+       struct macro    *m = &KeyMacro;
+
+       UpdModLine = YES;
+       if (InMacDefine) {
+               message("Keyboard macro defined.");
+               InMacDefine = NO;
+
+               /* try and strip off the key sequence that invoked us */
+               cp = &m->m_body[m->m_len - 2];
+               if (PrefChar(*cp))
+                       m->m_len -= 2;
+               else if (commands[cp[1]].c_proc == Forget)
+                       m->m_len -= 1;
+       } else
+               complain("[end-kbd-macro: not currently defining macro!]");
+}
+
+void
+ExecMacro()
+{
+       do_macro(&KeyMacro);
+}
+
+void
+MacInter()
+{
+       if (!Asking)
+               return;
+       Interactive = 1;
+}
+
+int
+ModMacs()
+{
+       register struct macro   *m;
+
+       for (m = macros->m_nextm; m != 0; m = m->m_nextm)
+               if (m->m_flags & SAVE)
+                       return YES;
+       return NO;
+}
+
+data_obj *
+findmac(prompt)
+char   *prompt;
+{
+       char    *strings[100];
+       register char   **strs = strings;
+       register int    com;
+       register struct macro   *m = macros;
+
+       for (; m != 0; m = m->m_nextm)
+               *strs++ = m->Name;
+       *strs = 0;
+
+       if ((com = complete(strings, prompt, NOTHING)) < 0)
+               return 0;
+       m = macros;
+       while (--com >= 0)
+               m = m->m_nextm;
+       return (data_obj *) m;
+}
+
+void
+DelMacro()
+{
+       struct macro    *m;
+
+       if ((m = (struct macro *) findmac(ProcFmt)) == 0)
+               return;
+       if (m == &KeyMacro)
+               complain("[It's illegal to delete the keyboard-macro!]");
+       del_mac(m);
+}