summaryrefslogtreecommitdiff
path: root/ex_cmdsub.c
diff options
context:
space:
mode:
Diffstat (limited to 'ex_cmdsub.c')
-rw-r--r--ex_cmdsub.c1443
1 files changed, 1443 insertions, 0 deletions
diff --git a/ex_cmdsub.c b/ex_cmdsub.c
new file mode 100644
index 0000000..1f798d7
--- /dev/null
+++ b/ex_cmdsub.c
@@ -0,0 +1,1443 @@
+/*
+ * This code contains changes by
+ * Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
+ *
+ * Conditions 1, 2, and 4 and the no-warranty notice below apply
+ * to these changes.
+ *
+ *
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * Redistributions of source code and documentation must retain the
+ * above copyright notice, this list of conditions and the following
+ * disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed or owned by Caldera
+ * International, Inc.
+ * Neither the name of Caldera International, Inc. nor the names of
+ * other contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
+ * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+#ifdef DOSCCS
+static char sccsid[] = "@(#)ex_cmdsub.c 1.29 (gritter) 2/17/05";
+#endif
+#endif
+
+/* from ex_cmdsub.c 7.7 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_argv.h"
+#include "ex_temp.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * Command mode subroutines implementing
+ * append, args, copy, delete, join, move, put,
+ * shift, tag, yank, z and undo
+ */
+
+bool endline = 1;
+line *tad1;
+static int jnoop(void);
+
+/*
+ * Append after line a lines returned by function f.
+ * Be careful about intermediate states to avoid scramble
+ * if an interrupt comes in.
+ */
+int
+append(int (*f)(void), line *a)
+{
+ register line *a1, *a2, *rdot;
+ int nline;
+
+ nline = 0;
+ dot = a;
+ if(FIXUNDO && !inopen && f!=getsub) {
+ undap1 = undap2 = dot + 1;
+ undkind = UNDCHANGE;
+ }
+ while ((*f)() == 0) {
+ if (truedol >= endcore) {
+ if (morelines() < 0) {
+ if (FIXUNDO && f == getsub) {
+ undap1 = addr1;
+ undap2 = addr2 + 1;
+ }
+ error(catgets(catd, 1, 39,
+ "Out of memory@- too many lines in file"));
+ }
+ }
+ nline++;
+ a1 = truedol + 1;
+ a2 = a1 + 1;
+ dot++;
+ undap2++;
+ dol++;
+ unddol++;
+ truedol++;
+ for (rdot = dot; a1 > rdot;)
+ *--a2 = *--a1;
+ *rdot = 0;
+ putmark(rdot);
+ if (f == gettty) {
+ dirtcnt++;
+ TSYNC();
+ }
+ }
+ return (nline);
+}
+
+void
+appendnone(void)
+{
+
+ if(FIXUNDO) {
+ undkind = UNDCHANGE;
+ undap1 = undap2 = addr1;
+ }
+}
+
+/*
+ * Print out the argument list, with []'s around the current name.
+ */
+void
+pargs(void)
+{
+ register char **av = argv0, *as = args0;
+ register int ac;
+
+ for (ac = 0; ac < argc0; ac++) {
+ if (ac != 0)
+ putchar(' ' | QUOTE);
+ if (ac + argc == argc0 - 1)
+ printf("[");
+ lprintf("%s", as);
+ if (ac + argc == argc0 - 1)
+ printf("]");
+ as = av ? *++av : strend(as) + 1;
+ }
+ noonl();
+}
+
+/*
+ * Delete lines; two cases are if we are really deleting,
+ * more commonly we are just moving lines to the undo save area.
+ */
+void
+delete(int hush)
+{
+ register line *a1, *a2;
+
+ nonzero();
+ if(FIXUNDO) {
+ register shand dsavint;
+
+#ifdef TRACE
+ if (trace)
+ vudump("before delete");
+#endif
+ change();
+ dsavint = signal(SIGINT, SIG_IGN);
+ undkind = UNDCHANGE;
+ a1 = addr1;
+ squish();
+ a2 = addr2;
+ if (a2++ != dol) {
+ reverse(a1, a2);
+ reverse(a2, dol + 1);
+ reverse(a1, dol + 1);
+ }
+ dol -= a2 - a1;
+ unddel = a1 - 1;
+ if (a1 > dol)
+ a1 = dol;
+ dot = a1;
+ pkill[0] = pkill[1] = 0;
+ signal(SIGINT, dsavint);
+#ifdef TRACE
+ if (trace)
+ vudump("after delete");
+#endif
+ } else {
+ register line *a3;
+ register int i;
+
+ change();
+ a1 = addr1;
+ a2 = addr2 + 1;
+ a3 = truedol;
+ i = a2 - a1;
+ unddol -= i;
+ undap2 -= i;
+ dol -= i;
+ truedol -= i;
+ do
+ *a1++ = *a2++;
+ while (a2 <= a3);
+ a1 = addr1;
+ if (a1 > dol)
+ a1 = dol;
+ dot = a1;
+ }
+ if (!hush)
+ killed();
+}
+
+void
+deletenone(void)
+{
+
+ if(FIXUNDO) {
+ undkind = UNDCHANGE;
+ squish();
+ unddel = addr1;
+ }
+}
+
+/*
+ * Crush out the undo save area, moving the open/visual
+ * save area down in its place.
+ */
+void
+squish(void)
+{
+ register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
+
+ if(FIXUNDO) {
+ if (inopen == -1)
+ return;
+ if (a1 < a2 && a2 < a3)
+ do
+ *a1++ = *a2++;
+ while (a2 < a3);
+ truedol -= unddol - dol;
+ unddol = dol;
+ }
+}
+
+static int jcount;
+
+/*
+ * Join lines. Special hacks put in spaces, two spaces if
+ * preceding line ends with '.', or no spaces if next line starts with ).
+ */
+void
+join(int c)
+{
+ register line *a1;
+ register char *cp, *cp1;
+
+ cp = genbuf;
+ *cp = 0;
+ for (a1 = addr1; a1 <= addr2; a1++) {
+ getline(*a1);
+ cp1 = linebuf;
+ if (a1 != addr1 && c == 0) {
+ while (*cp1 == ' ' || *cp1 == '\t')
+ cp1++;
+ if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
+ if (*cp1 != ')') {
+ *cp++ = ' ';
+ if (cp[-2] == '.')
+ *cp++ = ' ';
+ }
+ }
+ }
+ while (*cp++ = *cp1++)
+ if (cp > &genbuf[LBSIZE-2])
+ error(catgets(catd, 1, 40,
+ "Line overflow|Result line of join would be too long"));
+ cp--;
+ }
+ strcLIN(genbuf);
+ delete(0);
+ jcount = 1;
+ if (FIXUNDO)
+ undap1 = undap2 = addr1;
+ ignore(append(jnoop, --addr1));
+ if (FIXUNDO)
+ vundkind = VMANY;
+}
+
+static int
+jnoop(void)
+{
+
+ return(--jcount);
+}
+
+/*
+ * Move and copy lines. Hard work is done by move1 which
+ * is also called by undo.
+ */
+
+void
+move1(int cflag, line *addrt)
+{
+ register line *adt, *ad1, *ad2;
+ int lines;
+
+ adt = addrt;
+ lines = (addr2 - addr1) + 1;
+ if (cflag) {
+ tad1 = addr1;
+ ad1 = dol;
+ ignore(append(getcopy, ad1++));
+ ad2 = dol;
+ } else {
+ ad2 = addr2;
+ for (ad1 = addr1; ad1 <= ad2;)
+ *ad1++ &= ~01;
+ ad1 = addr1;
+ }
+ ad2++;
+ if (adt < ad1) {
+ if (adt + 1 == ad1 && !cflag && !inglobal)
+ error(catgets(catd, 1, 41,
+ "That move would do nothing!"));
+ dot = adt + (ad2 - ad1);
+ if (++adt != ad1) {
+ reverse(adt, ad1);
+ reverse(ad1, ad2);
+ reverse(adt, ad2);
+ }
+ } else if (adt >= ad2) {
+ dot = adt++;
+ reverse(ad1, ad2);
+ reverse(ad2, adt);
+ reverse(ad1, adt);
+ } else
+ error(catgets(catd, 1, 42, "Move to a moved line"));
+ change();
+ if (!inglobal)
+ if(FIXUNDO) {
+ if (cflag) {
+ undap1 = addrt + 1;
+ undap2 = undap1 + lines;
+ deletenone();
+ } else {
+ undkind = UNDMOVE;
+ undap1 = addr1;
+ undap2 = addr2;
+ unddel = addrt;
+ squish();
+ }
+ }
+}
+
+void
+move(void)
+{
+ register line *adt;
+ bool iscopy = 0;
+
+ if (Command[0] == 'm') {
+ setdot1();
+ markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
+ } else {
+ iscopy++;
+ setdot();
+ }
+ nonzero();
+ adt = address((char*)0);
+ if (adt == 0)
+ serror(catgets(catd, 1, 43,
+ "%s where?|%s requires a trailing address"), Command);
+ newline();
+ move1(iscopy, adt);
+ killed();
+}
+
+int
+getcopy(void)
+{
+
+ if (tad1 > addr2)
+ return (EOF);
+ getline(*tad1++);
+ return (0);
+}
+
+/*
+ * Put lines in the buffer from the undo save area.
+ */
+int
+getput(void)
+{
+
+ if (tad1 > unddol)
+ return (EOF);
+ getline(*tad1++);
+ tad1++;
+ return (0);
+}
+
+/*ARGSUSED*/
+void
+put(int unused)
+{
+ register int cnt;
+
+ if (!FIXUNDO)
+ error(catgets(catd, 1, 44, "Cannot put inside global/macro"));
+ cnt = unddol - dol;
+ if (cnt && inopen && pkill[0] && pkill[1]) {
+ pragged(1);
+ return;
+ }
+ tad1 = dol + 1;
+ ignore(append(getput, addr2));
+ undkind = UNDPUT;
+ notecnt = cnt;
+ netchange(cnt);
+}
+
+/*
+ * A tricky put, of a group of lines in the middle
+ * of an existing line. Only from open/visual.
+ * Argument says pkills have meaning, e.g. called from
+ * put; it is 0 on calls from putreg.
+ */
+void
+pragged(int kill)
+{
+ extern char *cursor;
+ register char *gp = &genbuf[cursor - linebuf];
+
+ /*
+ * This kind of stuff is TECO's forte.
+ * We just grunge along, since it cuts
+ * across our line-oriented model of the world
+ * almost scrambling our addled brain.
+ */
+ if (!kill)
+ getDOT();
+ strcpy(genbuf, linebuf);
+ getline(*unddol);
+ if (kill)
+ *pkill[1] = 0;
+ strcat(linebuf, gp);
+ putmark(unddol);
+ getline(dol[1]);
+ if (kill)
+ strcLIN(pkill[0]);
+ safecp(gp, linebuf, sizeof genbuf - (gp - genbuf), "Line too long");
+ strcLIN(genbuf);
+ putmark(dol+1);
+ undkind = UNDCHANGE;
+ undap1 = dot;
+ undap2 = dot + 1;
+ unddel = dot - 1;
+ undo(1);
+}
+
+/*
+ * Shift lines, based on c.
+ * If c is neither < nor >, then this is a lisp aligning =.
+ */
+void
+shift(int c, int cnt)
+{
+ register line *addr;
+ register char *cp = NULL;
+ char *dp;
+ register int i;
+
+ if(FIXUNDO)
+ save12(), undkind = UNDCHANGE;
+ cnt *= value(SHIFTWIDTH);
+ for (addr = addr1; addr <= addr2; addr++) {
+ dot = addr;
+#ifdef LISPCODE
+ if (c == '=' && addr == addr1 && addr != addr2)
+ continue;
+#endif
+ getDOT();
+ i = whitecnt(linebuf);
+ switch (c) {
+
+ case '>':
+ if (linebuf[0] == 0)
+ continue;
+ cp = genindent(i + cnt);
+ break;
+
+ case '<':
+ if (i == 0)
+ continue;
+ i -= cnt;
+ cp = i > 0 ? genindent(i) : genbuf;
+ break;
+
+#ifdef LISPCODE
+ default:
+ i = lindent(addr);
+ getDOT();
+ cp = genindent(i);
+ break;
+#endif
+ }
+ if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
+ error(catgets(catd, 1, 45,
+ "Line too long|Result line after shift would be too long"));
+ CP(cp, dp);
+ strcLIN(genbuf);
+ putmark(addr);
+ }
+ killed();
+}
+
+/*
+ * Find a tag in the tags file.
+ * Most work here is in parsing the tags file itself.
+ */
+void
+tagfind(bool quick)
+{
+ char cmdbuf[BUFSIZ];
+ char filebuf[FNSIZE];
+ char tagfbuf[128];
+ register int c, d;
+ bool samef = 1;
+ int tfcount = 0;
+ int omagic;
+ int owrapscan;
+ char *fn, *fne;
+ struct stat sbuf;
+ char *savefirstpat = NULL;
+ int ofailed;
+#ifdef FASTTAG
+ int ft_iof;
+ char ft_iofbuf[MAXBSIZE];
+ off_t mid; /* assumed byte offset */
+ off_t top, bot; /* length of tag file */
+#endif
+
+ omagic = value(MAGIC);
+ owrapscan = value(WRAPSCAN);
+ ofailed = failed;
+ failed = 1;
+ if (!skipend()) {
+ register char *lp = lasttag;
+
+ while (!is_white(peekchar()) && !endcmd(peekchar()))
+ if (lp < &lasttag[sizeof lasttag - 2])
+ *lp++ = getchar();
+ else
+ ignchar();
+ *lp++ = 0;
+ if (!endcmd(peekchar()))
+badtag:
+ error(catgets(catd, 1, 46,
+ "Bad tag|Give one tag per line"));
+ } else if (lasttag[0] == 0)
+ error(catgets(catd, 1, 47, "No previous tag"));
+ c = getchar();
+ if (!endcmd(c))
+ goto badtag;
+ if (c == EOF)
+ ungetchar(c);
+ clrstats();
+
+ /*
+ * Loop once for each file in tags "path".
+ */
+ safecp(tagfbuf, svalue(TAGS), sizeof tagfbuf, "Tag too long");
+ fne = tagfbuf - 1;
+ while (fne) {
+ fn = ++fne;
+ while (*fne && *fne != ' ')
+ fne++;
+ if (*fne == 0)
+ fne = 0; /* done, quit after this time */
+ else
+ *fne = 0; /* null terminate filename */
+#ifdef FASTTAG
+ ft_iof = topen(fn, ft_iofbuf);
+ if (ft_iof == -1)
+ continue;
+ tfcount++;
+ fstat(ft_iof, &sbuf);
+ top = sbuf.st_size;
+ if (top == (off_t) 0 )
+ top = (off_t) -1;
+ bot = (off_t) 0;
+ while (top >= bot) {
+#else
+ /*
+ * Avoid stdio and scan tag file linearly.
+ */
+ io = open(fn, O_RDONLY);
+ if (io<0)
+ continue;
+ tfcount++;
+ if (fstat(io, &sbuf) < 0 || sbuf.st_blksize > LBSIZE)
+ bsize = LBSIZE;
+ else {
+ bsize = sbuf.st_blksize;
+ if (bsize <= 0)
+ bsize = LBSIZE;
+ }
+ while (getfile() == 0) {
+#endif
+ /* loop for each tags file entry */
+ register char *cp = linebuf;
+ register char *lp = lasttag;
+ char *oglobp;
+
+#ifdef FASTTAG
+ mid = (top + bot) / 2;
+ tseek(ft_iof, mid);
+ if (mid > 0) /* to get first tag in file to work */
+ /* scan to next \n */
+ if(tgets(linebuf, sizeof linebuf, ft_iof)==0)
+ goto goleft;
+ /* get the line itself */
+ if(tgets(linebuf, sizeof linebuf, ft_iof)==0)
+ goto goleft;
+#ifdef TDEBUG
+ printf("tag: %o %o %o %s\n", bot, mid, top, linebuf);
+#endif
+#endif
+ while (*cp && *lp == *cp)
+ cp++, lp++;
+ if ((*lp || !is_white(*cp)) && (value(TAGLENGTH)==0 ||
+ lp-lasttag < value(TAGLENGTH))) {
+#ifdef FASTTAG
+ if (*lp > *cp)
+ bot = mid + 1;
+ else
+goleft:
+ top = mid - 1;
+#endif
+ /* Not this tag. Try the next */
+ continue;
+ }
+
+ /*
+ * We found the tag. Decode the line in the file.
+ */
+#ifdef FASTTAG
+ tclose(ft_iof);
+#else
+ close(io);
+#endif
+ /* Rest of tag if abbreviated */
+ while (!is_white(*cp))
+ cp++;
+
+ /* name of file */
+ while (*cp && is_white(*cp))
+ cp++;
+ if (!*cp)
+badtags:
+ serror(catgets(catd, 1, 48,
+ "%s: Bad tags file entry"), lasttag);
+ lp = filebuf;
+ while (*cp && *cp != ' ' && *cp != '\t') {
+ if (lp < &filebuf[sizeof filebuf - 2])
+ *lp++ = *cp;
+ cp++;
+ }
+ *lp++ = 0;
+
+ if (*cp == 0)
+ goto badtags;
+ if (dol != zero) {
+ /*
+ * Save current position in 't for ^^ in visual.
+ */
+ names['t'-'a'] = *dot &~ 01;
+ if (inopen) {
+ extern char *ncols['z'-'a'+2];
+ extern char *cursor;
+
+ ncols['t'-'a'] = cursor;
+ }
+ }
+ safecp(cmdbuf, cp, sizeof cmdbuf, "command too long");
+ if (strcmp(filebuf, savedfile) || !edited) {
+ char cmdbuf2[sizeof filebuf + 10];
+
+ savefirstpat = firstpat;
+ firstpat = NULL;
+ /* Different file. Do autowrite & get it. */
+ if (!quick) {
+ ckaw();
+ if (chng && dol > zero)
+ error(catgets(catd, 1, 49,
+ "No write@since last change (:tag! overrides)"));
+ }
+ oglobp = globp;
+ strcpy(cmdbuf2, "e! ");
+ strcat(cmdbuf2, filebuf);
+ globp = cmdbuf2;
+ d = peekc; ungetchar(0);
+ commands(1, 1);
+ peekc = d;
+ globp = oglobp;
+ value(MAGIC) = omagic;
+ if (tflag > 0)
+ value(WRAPSCAN) = owrapscan;
+ samef = 0;
+ firstpat = savefirstpat;
+ }
+
+ /*
+ * Look for pattern in the current file.
+ */
+ oglobp = globp;
+ globp = cmdbuf;
+ d = peekc; ungetchar(0);
+ if (samef)
+ markpr(dot);
+ /*
+ * BUG: if it isn't found (user edited header
+ * line) we get left in nomagic mode.
+ */
+ value(MAGIC) = 0;
+ if (tflag > 0)
+ value(WRAPSCAN) = 1;
+ commands(1, 1);
+ failed = ofailed;
+ peekc = d;
+ globp = oglobp;
+ value(MAGIC) = omagic;
+ if (tflag > 0) {
+ value(WRAPSCAN) = owrapscan;
+ if (savefirstpat) {
+ globp = savefirstpat;
+ tflag = -1;
+ } else
+ tflag = 0;
+ }
+ return;
+ } /* end of "for each tag in file" */
+
+ /*
+ * No such tag in this file. Close it and try the next.
+ */
+#ifdef FASTTAG
+ tclose(ft_iof);
+#else
+ close(io);
+#endif
+ } /* end of "for each file in path" */
+ if (tfcount <= 0)
+ error(catgets(catd, 1, 50, "No tags file"));
+ else
+ serror(catgets(catd, 1, 51,
+ "%s: No such tag@in tags file"), lasttag);
+}
+
+/*
+ * Save lines from addr1 thru addr2 as though
+ * they had been deleted.
+ */
+/*ARGSUSED*/
+void
+yank(int unused)
+{
+
+ if (!FIXUNDO)
+ error(catgets(catd, 1, 52, "Can't yank inside global/macro"));
+ save12();
+ undkind = UNDNONE;
+ killcnt(addr2 - addr1 + 1);
+}
+
+/*
+ * z command; print windows of text in the file.
+ *
+ * If this seems unreasonably arcane, the reasons
+ * are historical. This is one of the first commands
+ * added to the first ex (then called en) and the
+ * number of facilities here were the major advantage
+ * of en over ed since they allowed more use to be
+ * made of fast terminals w/o typing .,.22p all the time.
+ */
+bool zhadpr;
+bool znoclear;
+short zweight;
+
+void
+zop(int hadpr)
+{
+ register int c, lines, op;
+ bool excl;
+
+ zhadpr = hadpr;
+ notempty();
+ znoclear = 0;
+ zweight = 0;
+ excl = exclam();
+ switch (c = op = getchar()) {
+
+ case '^':
+ zweight = 1;
+ case '-':
+ case '+':
+ while (peekchar() == op) {
+ ignchar();
+ zweight++;
+ }
+ case '=':
+ case '.':
+ c = getchar();
+ break;
+
+ case EOF:
+ znoclear++;
+ break;
+
+ default:
+ op = 0;
+ break;
+ }
+ if (isdigit(c)) {
+ lines = c - '0';
+ for(;;) {
+ c = getchar();
+ if (!isdigit(c))
+ break;
+ lines *= 10;
+ lines += c - '0';
+ }
+ if (lines < TLINES)
+ znoclear++;
+ value(WINDOW) = lines;
+ if (op == '=')
+ lines += 2;
+ } else
+ lines = op == EOF ? value(SCROLL) : excl ? TLINES - 1 : 2*value(SCROLL);
+ if (inopen || c != EOF) {
+ ungetchar(c);
+ newline();
+ }
+ addr1 = addr2;
+ if (addr2 == 0 && dot < dol && op == 0)
+ addr1 = addr2 = dot+1;
+ setdot();
+ zop2(lines, op);
+}
+
+static void
+splitit(void)
+{
+ register int l;
+
+ for (l = TCOLUMNS > 80 ? 40 : TCOLUMNS / 2; l > 0; l--)
+ putchar('-');
+ putnl();
+}
+
+void
+zop2(register int lines, register int op)
+{
+ register line *split;
+
+ split = NULL;
+ switch (op) {
+
+ case EOF:
+ if (addr2 == dol)
+ error(catgets(catd, 1, 53, "\nAt EOF"));
+ case '+':
+ if (addr2 == dol)
+ error(catgets(catd, 1, 54, "At EOF"));
+ addr2 += lines * zweight;
+ if (addr2 > dol)
+ error(catgets(catd, 1, 55, "Hit BOTTOM"));
+ addr2++;
+ default:
+ addr1 = addr2;
+ addr2 += lines-1;
+ dot = addr2;
+ break;
+
+ case '=':
+ case '.':
+ znoclear = 0;
+ lines--;
+ lines >>= 1;
+ if (op == '=')
+ lines--;
+ addr1 = addr2 - lines;
+ if (op == '=')
+ dot = split = addr2;
+ addr2 += lines;
+ if (op == '.') {
+ markDOT();
+ dot = addr2;
+ }
+ break;
+
+ case '^':
+ case '-':
+ addr2 -= lines * zweight;
+ if (addr2 < one)
+ error(catgets(catd, 1, 56, "Hit TOP"));
+ lines--;
+ addr1 = addr2 - lines;
+ dot = addr2;
+ break;
+ }
+ if (addr1 <= zero)
+ addr1 = one;
+ if (addr2 > dol)
+ addr2 = dol;
+ if (dot > dol)
+ dot = dol;
+ if (addr1 > addr2)
+ return;
+ if (op == EOF && zhadpr) {
+ getline(*addr1);
+ putchar('\r' | QUOTE);
+ shudclob = 1;
+ } else if (znoclear == 0 && CL != NOSTR && !inopen) {
+ flush1();
+ vclear();
+ }
+ if (addr2 - addr1 > 1)
+ pstart();
+ if (split) {
+ plines(addr1, split - 1, 0);
+ splitit();
+ plines(split, split, 0);
+ splitit();
+ addr1 = split + 1;
+ }
+ plines(addr1, addr2, 0);
+}
+
+void
+plines(line *adr1, register line *adr2, bool movedot)
+{
+ register line *addr;
+
+ pofix();
+ for (addr = adr1; addr <= adr2; addr++) {
+ getline(*addr);
+ pline(lineno(addr));
+ if (inopen) {
+ putchar('\n' | QUOTE);
+ }
+ if (movedot)
+ dot = addr;
+ }
+}
+
+void
+pofix(void)
+{
+
+ if (inopen && Outchar != termchar) {
+ vnfl();
+ setoutt();
+ }
+}
+
+/*
+ * Be (almost completely) sure there really
+ * was a change, before claiming to undo.
+ */
+void
+somechange(void)
+{
+ register line *ip, *jp;
+
+ switch (undkind) {
+
+ case UNDMOVE:
+ return;
+
+ case UNDCHANGE:
+ if (undap1 == undap2 && dol == unddol)
+ break;
+ return;
+
+ case UNDPUT:
+ if (undap1 != undap2)
+ return;
+ break;
+
+ case UNDALL:
+ if (unddol - dol != lineDOL())
+ return;
+ for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
+ if ((*ip &~ 01) != (*jp &~ 01))
+ return;
+ break;
+
+ case UNDNONE:
+ error(catgets(catd, 1, 57, "Nothing to undo"));
+ }
+ error(catgets(catd, 1, 58,
+ "Nothing changed|Last undoable command didn't change anything"));
+}
+
+/*
+ * Dudley doright to the rescue.
+ * Undo saves the day again.
+ * A tip of the hatlo hat to Warren Teitleman
+ * who made undo as useful as do.
+ *
+ * Command level undo works easily because
+ * the editor has a unique temporary file
+ * index for every line which ever existed.
+ * We don't have to save large blocks of text,
+ * only the indices which are small. We do this
+ * by moving them to after the last line in the
+ * line buffer array, and marking down info
+ * about whence they came.
+ *
+ * Undo is its own inverse.
+ */
+void
+undo(bool c)
+{
+ register int i, j;
+ register line *jp, *kp;
+ line *dolp1, *newdol, *newadot;
+
+#ifdef TRACE
+ if (trace)
+ vudump("before undo");
+#endif
+ if (inglobal && inopen <= 0)
+ error(catgets(catd, 1, 59, "Can't undo in global@commands"));
+ if (!c)
+ somechange();
+ pkill[0] = pkill[1] = 0;
+ change();
+ if (undkind == UNDMOVE) {
+ /*
+ * Command to be undone is a move command.
+ * This is handled as a special case by noting that
+ * a move "a,b m c" can be inverted by another move.
+ */
+ if ((i = (jp = unddel) - undap2) > 0) {
+ /*
+ * when c > b inverse is a+(c-b),c m a-1
+ */
+ addr2 = jp;
+ addr1 = (jp = undap1) + i;
+ unddel = jp-1;
+ } else {
+ /*
+ * when b > c inverse is c+1,c+1+(b-a) m b
+ */
+ addr1 = ++jp;
+ addr2 = jp + ((unddel = undap2) - undap1);
+ }
+ kp = undap1;
+ move1(0, unddel);
+ dot = kp;
+ Command = "move";
+ killed();
+ } else {
+ int cnt;
+
+ newadot = dot;
+ cnt = lineDOL();
+ newdol = dol;
+ dolp1 = dol + 1;
+ /*
+ * If a mark is pointing to a line between undap1 and
+ * undap2-1, it would be lost (i.e. pointing into the
+ * block between dolp and undol) after the undo. Thus
+ * these marks have to be changed to point to the line
+ * after dolp1 that is restored later during this undo
+ * operation.
+ */
+ if (anymarks)
+ for (i = 0; &undap1[i] < undap2; i++)
+ for (j = 0; j <= 'z'-'a'; j++)
+ if (names[j] == (undap1[i] & ~01))
+ names[j] = dolp1[i] & ~01;
+ /*
+ * Command to be undone is a non-move.
+ * All such commands are treated as a combination of
+ * a delete command and a append command.
+ * We first move the lines appended by the last command
+ * from undap1 to undap2-1 so that they are just before the
+ * saved deleted lines.
+ */
+ if ((i = (kp = undap2) - (jp = undap1)) > 0) {
+ if (kp != dolp1) {
+ reverse(jp, kp);
+ reverse(kp, dolp1);
+ reverse(jp, dolp1);
+ }
+ /*
+ * Account for possible backward motion of target
+ * for restoration of saved deleted lines.
+ */
+ if (unddel >= jp)
+ unddel -= i;
+ newdol -= i;
+ /*
+ * For the case where no lines are restored, dot
+ * is the line before the first line deleted.
+ */
+ dot = jp-1;
+ }
+ /*
+ * Now put the deleted lines, if any, back where they were.
+ * Basic operation is: dol+1,unddol m unddel
+ */
+ if (undkind == UNDPUT) {
+ unddel = undap1 - 1;
+ squish();
+ }
+ jp = unddel + 1;
+ if ((i = (kp = unddol) - dol) > 0) {
+ if (jp != dolp1) {
+ reverse(jp, dolp1);
+ reverse(dolp1, ++kp);
+ reverse(jp, kp);
+ }
+ /*
+ * Account for possible forward motion of the target
+ * for restoration of the deleted lines.
+ */
+ if (undap1 >= jp)
+ undap1 += i;
+ /*
+ * Dot is the first resurrected line.
+ */
+ dot = jp;
+ newdol += i;
+ }
+ /*
+ * Clean up so we are invertible
+ */
+ unddel = undap1 - 1;
+ undap1 = jp;
+ undap2 = jp + i;
+ dol = newdol;
+ netchHAD(cnt);
+ if (undkind == UNDALL) {
+ dot = undadot;
+ undadot = newadot;
+ } else
+ undkind = UNDCHANGE;
+ }
+ /*
+ * Defensive programming - after a munged undadot.
+ * Also handle empty buffer case.
+ */
+ if ((dot <= zero || dot > dol) && dot != dol)
+ dot = one;
+#ifdef TRACE
+ if (trace)
+ vudump("after undo");
+#endif
+}
+
+/*
+ * Map command:
+ * map src dest
+ */
+void
+mapcmd(int un, int ab)
+ /* int un; /\* true if this is unmap command */
+ /*int ab; /\* true if this is abbr command */
+{
+ char lhs[100], rhs[100]; /* max sizes resp. */
+ register char *p;
+ register int c; /* mjm: char --> int */
+ char *dname;
+ struct maps *mp; /* the map structure we are working on */
+
+ mp = ab ? abbrevs : exclam() ? immacs : arrows;
+ if (skipend()) {
+ int i;
+
+ /* print current mapping values */
+ if (peekchar() != EOF)
+ ignchar();
+ if (un)
+ error(catgets(catd, 1, 60, "Missing lhs"));
+ if (inopen)
+ pofix();
+ for (i=0; mp[i].mapto; i++)
+ if (mp[i].cap) {
+ lprintf("%s", mp[i].descr);
+ putchar('\t');
+ lprintf("%s", mp[i].cap);
+ putchar('\t');
+ lprintf("%s", mp[i].mapto);
+ putNFL();
+ }
+ return;
+ }
+
+ ignore(skipwh());
+ for (p=lhs; ; ) {
+ c = getchar();
+ if (c == CTRL('v')) {
+ c = getchar();
+ } else if (!un && any(c, " \t")) {
+ /* End of lhs */
+ break;
+ } else if (endcmd(c) && c!='"') {
+ ungetchar(c);
+ if (un) {
+ newline();
+ *p = 0;
+ addmac(lhs, NOSTR, NOSTR, mp);
+ return;
+ } else
+ error(catgets(catd, 1, 61, "Missing rhs"));
+ }
+ *p++ = c;
+ }
+ *p = 0;
+
+ if (skipend())
+ error(catgets(catd, 1, 62, "Missing rhs"));
+ for (p=rhs; ; ) {
+ c = getchar();
+ if (c == CTRL('v')) {
+ c = getchar();
+ } else if (endcmd(c) && c!='"') {
+ ungetchar(c);
+ break;
+ }
+ *p++ = c;
+ }
+ *p = 0;
+ newline();
+ /*
+ * Special hack for function keys: #1 means key f1, etc.
+ * If the terminal doesn't have function keys, we just use #1.
+ */
+ if (lhs[0] == '#') {
+ char *fnkey;
+ char funkey[3];
+
+ fnkey = fkey(lhs[1] - '0');
+ funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
+ if (fnkey)
+ strcpy(lhs, fnkey);
+ dname = funkey;
+ } else {
+ dname = lhs;
+ }
+ addmac(lhs,rhs,dname,mp);
+}
+
+/*
+ * Create the integer version of a macro string, for processing in visual
+ * mode. imapspace cannot overflow because an earlier overflow for mapspace
+ * would have been detected already.
+ */
+static void
+intmac(int **dp, char *cp)
+{
+ int c, n;
+
+ if (imsnext == NULL)
+ imsnext = imapspace;
+ *dp = imsnext;
+ for (;;) {
+ nextc(c, cp, n);
+ *imsnext++ = c;
+ if (c == 0)
+ break;
+ cp += n;
+ }
+}
+
+/*
+ * Add a macro definition to those that already exist. The sequence of
+ * chars "src" is mapped into "dest". If src is already mapped into something
+ * this overrides the mapping. There is no recursion. Unmap is done by
+ * using NOSTR for dest. Dname is what to show in listings. mp is
+ * the structure to affect (arrows, etc).
+ */
+void
+addmac1(register char *src,register char *dest,register char *dname,
+ register struct maps *mp, int force)
+{
+ register int slot, zer;
+
+#ifdef TRACE
+ if (trace)
+ fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
+#endif
+ if (dest && mp==arrows && !force) {
+ /* Make sure user doesn't screw himself */
+ /*
+ * Prevent tail recursion. We really should be
+ * checking to see if src is a suffix of dest
+ * but this makes mapping involving escapes that
+ * is reasonable mess up.
+ */
+ if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
+ error(catgets(catd, 1, 63, "No tail recursion"));
+ /*
+ * We don't let the user rob himself of ":", and making
+ * multi char words is a bad idea so we don't allow it.
+ * Note that if user sets mapinput and maps all of return,
+ * linefeed, and escape, he can screw himself. This is
+ * so weird I don't bother to check for it.
+ */
+ if (isalpha(src[0]&0377) && src[1] || any(src[0],":"))
+ error(catgets(catd, 1, 64,
+ "Too dangerous to map that"));
+ }
+ else if (dest) {
+ /* check for tail recursion in input mode: fussier */
+ if (eq(src, dest+strlen(dest)-strlen(src)))
+ error(catgets(catd, 1, 65, "No tail recursion"));
+ }
+ /*
+ * If the src were null it would cause the dest to
+ * be mapped always forever. This is not good.
+ */
+ if (!force && (src == NOSTR || src[0] == 0))
+ error(catgets(catd, 1, 66, "Missing lhs"));
+
+ /* see if we already have a def for src */
+ zer = -1;
+ for (slot=0; mp[slot].mapto; slot++) {
+ if (mp[slot].cap) {
+ if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
+ break; /* if so, reuse slot */
+ } else {
+ zer = slot; /* remember an empty slot */
+ }
+ }
+
+ if (dest == NOSTR) {
+ /* unmap */
+ if (mp[slot].cap) {
+ mp[slot].cap = NOSTR;
+ mp[slot].descr = NOSTR;
+ } else {
+ error(catgets(catd, 1, 67,
+ "Not mapped|That macro wasn't mapped"));
+ }
+ return;
+ }
+
+ /* reuse empty slot, if we found one and src isn't already defined */
+ if (zer >= 0 && mp[slot].mapto == 0)
+ slot = zer;
+
+ /* if not, append to end */
+ if (slot >= MAXNOMACS)
+ error(catgets(catd, 1, 68, "Too many macros"));
+ if (msnext == 0) /* first time */
+ msnext = mapspace;
+ /* Check is a bit conservative, we charge for dname even if reusing src */
+ if (msnext - mapspace + strlen(dest) + (src ? strlen(src) : 0) + strlen(dname) + 3 > MAXCHARMACS)
+ error(catgets(catd, 1, 69, "Too much macro text"));
+ if (src) {
+ CP(msnext, src);
+ mp[slot].cap = msnext;
+ msnext += strlen(src) + 1; /* plus 1 for null on the end */
+ intmac(&mp[slot].icap, src);
+ } else
+ mp[slot].cap = NULL;
+ CP(msnext, dest);
+ mp[slot].mapto = msnext;
+ msnext += strlen(dest) + 1;
+ if (dname) {
+ CP(msnext, dname);
+ mp[slot].descr = msnext;
+ msnext += strlen(dname) + 1;
+ } else {
+ /* default descr to string user enters */
+ mp[slot].descr = src;
+ }
+}
+
+/*
+ * Implements macros from command mode. c is the buffer to
+ * get the macro from.
+ */
+void
+cmdmac(char c)
+{
+ char macbuf[BUFSIZ];
+ line *ad, *a1, *a2;
+ char *oglobp;
+ short pk;
+ bool oinglobal;
+
+ lastmac = c;
+ oglobp = globp;
+ oinglobal = inglobal;
+ pk = peekc; peekc = 0;
+ if (inglobal < 2)
+ inglobal = 1;
+ regbuf(c, macbuf, sizeof(macbuf));
+ a1 = addr1; a2 = addr2;
+ for (ad=a1; ad<=a2; ad++) {
+ globp = macbuf;
+ dot = ad;
+ commands(1,1);
+ }
+ globp = oglobp;
+ inglobal = oinglobal;
+ peekc = pk;
+}