diff options
Diffstat (limited to 'ex_cmdsub.c')
-rw-r--r-- | ex_cmdsub.c | 1443 |
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; +} |