xref: /original-bsd/usr.bin/ex/ex_cmdsub.c (revision 2949201c)
1e4e787efSdist /*
2e4e787efSdist  * Copyright (c) 1980 Regents of the University of California.
3e4e787efSdist  * All rights reserved.  The Berkeley software License Agreement
4e4e787efSdist  * specifies the terms and conditions for redistribution.
5e4e787efSdist  */
6e4e787efSdist 
7e4e787efSdist #ifndef lint
8*2949201cSbostic static char *sccsid = "@(#)ex_cmdsub.c	7.9 (Berkeley) 01/02/88";
9e4e787efSdist #endif not lint
10e4e787efSdist 
1106fb5685Smark #include "ex.h"
1206fb5685Smark #include "ex_argv.h"
1306fb5685Smark #include "ex_temp.h"
1406fb5685Smark #include "ex_tty.h"
15d4cba322Smark #include "ex_vis.h"
1606fb5685Smark 
1706fb5685Smark /*
1806fb5685Smark  * Command mode subroutines implementing
1906fb5685Smark  *	append, args, copy, delete, join, move, put,
2006fb5685Smark  *	shift, tag, yank, z and undo
2106fb5685Smark  */
2206fb5685Smark 
2306fb5685Smark bool	endline = 1;
2406fb5685Smark line	*tad1;
25d4cba322Smark static	jnoop();
2606fb5685Smark 
2706fb5685Smark /*
2806fb5685Smark  * Append after line a lines returned by function f.
2906fb5685Smark  * Be careful about intermediate states to avoid scramble
3006fb5685Smark  * if an interrupt comes in.
3106fb5685Smark  */
3206fb5685Smark append(f, a)
3306fb5685Smark 	int (*f)();
3406fb5685Smark 	line *a;
3506fb5685Smark {
3606fb5685Smark 	register line *a1, *a2, *rdot;
3706fb5685Smark 	int nline;
3806fb5685Smark 
3906fb5685Smark 	nline = 0;
4006fb5685Smark 	dot = a;
41aecfa433Smark 	if(FIXUNDO && !inopen && f!=getsub) {
4206fb5685Smark 		undap1 = undap2 = dot + 1;
4306fb5685Smark 		undkind = UNDCHANGE;
4406fb5685Smark 	}
4506fb5685Smark 	while ((*f)() == 0) {
4606fb5685Smark 		if (truedol >= endcore) {
4706fb5685Smark 			if (morelines() < 0) {
48aecfa433Smark 				if (FIXUNDO && f == getsub) {
4906fb5685Smark 					undap1 = addr1;
5006fb5685Smark 					undap2 = addr2 + 1;
5106fb5685Smark 				}
5206fb5685Smark 				error("Out of memory@- too many lines in file");
5306fb5685Smark 			}
5406fb5685Smark 		}
5506fb5685Smark 		nline++;
5606fb5685Smark 		a1 = truedol + 1;
5706fb5685Smark 		a2 = a1 + 1;
5806fb5685Smark 		dot++;
5906fb5685Smark 		undap2++;
6006fb5685Smark 		dol++;
6106fb5685Smark 		unddol++;
6206fb5685Smark 		truedol++;
6306fb5685Smark 		for (rdot = dot; a1 > rdot;)
6406fb5685Smark 			*--a2 = *--a1;
6506fb5685Smark 		*rdot = 0;
6606fb5685Smark 		putmark(rdot);
6706fb5685Smark 		if (f == gettty) {
6806fb5685Smark 			dirtcnt++;
6906fb5685Smark 			TSYNC();
7006fb5685Smark 		}
7106fb5685Smark 	}
7206fb5685Smark 	return (nline);
7306fb5685Smark }
7406fb5685Smark 
7506fb5685Smark appendnone()
7606fb5685Smark {
7706fb5685Smark 
78aecfa433Smark 	if(FIXUNDO) {
7906fb5685Smark 		undkind = UNDCHANGE;
8006fb5685Smark 		undap1 = undap2 = addr1;
8106fb5685Smark 	}
8206fb5685Smark }
8306fb5685Smark 
8406fb5685Smark /*
8506fb5685Smark  * Print out the argument list, with []'s around the current name.
8606fb5685Smark  */
8706fb5685Smark pargs()
8806fb5685Smark {
8906fb5685Smark 	register char **av = argv0, *as = args0;
9006fb5685Smark 	register int ac;
9106fb5685Smark 
9206fb5685Smark 	for (ac = 0; ac < argc0; ac++) {
9306fb5685Smark 		if (ac != 0)
94c0a5300eSconrad 			ex_putchar(' ' | QUOTE);
9506fb5685Smark 		if (ac + argc == argc0 - 1)
96c0a5300eSconrad 			ex_printf("[");
9706fb5685Smark 		lprintf("%s", as);
9806fb5685Smark 		if (ac + argc == argc0 - 1)
99c0a5300eSconrad 			ex_printf("]");
10006fb5685Smark 		as = av ? *++av : strend(as) + 1;
10106fb5685Smark 	}
10206fb5685Smark 	noonl();
10306fb5685Smark }
10406fb5685Smark 
10506fb5685Smark /*
10606fb5685Smark  * Delete lines; two cases are if we are really deleting,
10706fb5685Smark  * more commonly we are just moving lines to the undo save area.
10806fb5685Smark  */
109c0a5300eSconrad ex_delete(hush)
11006fb5685Smark 	bool hush;
11106fb5685Smark {
11206fb5685Smark 	register line *a1, *a2;
11306fb5685Smark 
11406fb5685Smark 	nonzero();
115aecfa433Smark 	if(FIXUNDO) {
11606fb5685Smark 		register int (*dsavint)();
11706fb5685Smark 
118269e4ba1Sdist #ifdef TRACE
119269e4ba1Sdist 		if (trace)
120269e4ba1Sdist 			vudump("before delete");
121269e4ba1Sdist #endif
12206fb5685Smark 		change();
12306fb5685Smark 		dsavint = signal(SIGINT, SIG_IGN);
12406fb5685Smark 		undkind = UNDCHANGE;
12506fb5685Smark 		a1 = addr1;
12606fb5685Smark 		squish();
12706fb5685Smark 		a2 = addr2;
12806fb5685Smark 		if (a2++ != dol) {
12906fb5685Smark 			reverse(a1, a2);
13006fb5685Smark 			reverse(a2, dol + 1);
13106fb5685Smark 			reverse(a1, dol + 1);
13206fb5685Smark 		}
13306fb5685Smark 		dol -= a2 - a1;
13406fb5685Smark 		unddel = a1 - 1;
13506fb5685Smark 		if (a1 > dol)
13606fb5685Smark 			a1 = dol;
13706fb5685Smark 		dot = a1;
13806fb5685Smark 		pkill[0] = pkill[1] = 0;
13906fb5685Smark 		signal(SIGINT, dsavint);
140269e4ba1Sdist #ifdef TRACE
141269e4ba1Sdist 		if (trace)
142269e4ba1Sdist 			vudump("after delete");
143269e4ba1Sdist #endif
14406fb5685Smark 	} else {
14506fb5685Smark 		register line *a3;
14606fb5685Smark 		register int i;
14706fb5685Smark 
14806fb5685Smark 		change();
14906fb5685Smark 		a1 = addr1;
15006fb5685Smark 		a2 = addr2 + 1;
15106fb5685Smark 		a3 = truedol;
15206fb5685Smark 		i = a2 - a1;
15306fb5685Smark 		unddol -= i;
15406fb5685Smark 		undap2 -= i;
15506fb5685Smark 		dol -= i;
15606fb5685Smark 		truedol -= i;
15706fb5685Smark 		do
15806fb5685Smark 			*a1++ = *a2++;
15906fb5685Smark 		while (a2 <= a3);
16006fb5685Smark 		a1 = addr1;
16106fb5685Smark 		if (a1 > dol)
16206fb5685Smark 			a1 = dol;
16306fb5685Smark 		dot = a1;
16406fb5685Smark 	}
16506fb5685Smark 	if (!hush)
16606fb5685Smark 		killed();
16706fb5685Smark }
16806fb5685Smark 
16906fb5685Smark deletenone()
17006fb5685Smark {
17106fb5685Smark 
172aecfa433Smark 	if(FIXUNDO) {
17306fb5685Smark 		undkind = UNDCHANGE;
17406fb5685Smark 		squish();
17506fb5685Smark 		unddel = addr1;
17606fb5685Smark 	}
17706fb5685Smark }
17806fb5685Smark 
17906fb5685Smark /*
18006fb5685Smark  * Crush out the undo save area, moving the open/visual
18106fb5685Smark  * save area down in its place.
18206fb5685Smark  */
18306fb5685Smark squish()
18406fb5685Smark {
18506fb5685Smark 	register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
18606fb5685Smark 
187aecfa433Smark 	if(FIXUNDO) {
18806fb5685Smark 		if (inopen == -1)
18906fb5685Smark 			return;
19006fb5685Smark 		if (a1 < a2 && a2 < a3)
19106fb5685Smark 			do
19206fb5685Smark 				*a1++ = *a2++;
19306fb5685Smark 			while (a2 < a3);
19406fb5685Smark 		truedol -= unddol - dol;
19506fb5685Smark 		unddol = dol;
19606fb5685Smark 	}
197aecfa433Smark }
19806fb5685Smark 
19906fb5685Smark /*
20006fb5685Smark  * Join lines.  Special hacks put in spaces, two spaces if
20106fb5685Smark  * preceding line ends with '.', or no spaces if next line starts with ).
20206fb5685Smark  */
20306fb5685Smark static	int jcount, jnoop();
20406fb5685Smark 
20506fb5685Smark join(c)
20606fb5685Smark 	int c;
20706fb5685Smark {
20806fb5685Smark 	register line *a1;
20906fb5685Smark 	register char *cp, *cp1;
21006fb5685Smark 
21106fb5685Smark 	cp = genbuf;
21206fb5685Smark 	*cp = 0;
21306fb5685Smark 	for (a1 = addr1; a1 <= addr2; a1++) {
21406fb5685Smark 		getline(*a1);
21506fb5685Smark 		cp1 = linebuf;
21606fb5685Smark 		if (a1 != addr1 && c == 0) {
21706fb5685Smark 			while (*cp1 == ' ' || *cp1 == '\t')
21806fb5685Smark 				cp1++;
21906fb5685Smark 			if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
22006fb5685Smark 				if (*cp1 != ')') {
22106fb5685Smark 					*cp++ = ' ';
22206fb5685Smark 					if (cp[-2] == '.')
22306fb5685Smark 						*cp++ = ' ';
22406fb5685Smark 				}
22506fb5685Smark 			}
22606fb5685Smark 		}
22706fb5685Smark 		while (*cp++ = *cp1++)
22806fb5685Smark 			if (cp > &genbuf[LBSIZE-2])
22906fb5685Smark 				error("Line overflow|Result line of join would be too long");
23006fb5685Smark 		cp--;
23106fb5685Smark 	}
23206fb5685Smark 	strcLIN(genbuf);
233c0a5300eSconrad 	ex_delete(0);
23406fb5685Smark 	jcount = 1;
235d4cba322Smark 	if (FIXUNDO)
236d4cba322Smark 		undap1 = undap2 = addr1;
23706fb5685Smark 	ignore(append(jnoop, --addr1));
238d4cba322Smark 	if (FIXUNDO)
239d4cba322Smark 		vundkind = VMANY;
24006fb5685Smark }
24106fb5685Smark 
24206fb5685Smark static
24306fb5685Smark jnoop()
24406fb5685Smark {
24506fb5685Smark 
24606fb5685Smark 	return(--jcount);
24706fb5685Smark }
24806fb5685Smark 
24906fb5685Smark /*
25006fb5685Smark  * Move and copy lines.  Hard work is done by move1 which
25106fb5685Smark  * is also called by undo.
25206fb5685Smark  */
25306fb5685Smark int	getcopy();
25406fb5685Smark 
25506fb5685Smark move()
25606fb5685Smark {
25706fb5685Smark 	register line *adt;
25806fb5685Smark 	bool iscopy = 0;
25906fb5685Smark 
26006fb5685Smark 	if (Command[0] == 'm') {
26106fb5685Smark 		setdot1();
26206fb5685Smark 		markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
26306fb5685Smark 	} else {
26406fb5685Smark 		iscopy++;
26506fb5685Smark 		setdot();
26606fb5685Smark 	}
26706fb5685Smark 	nonzero();
268269e4ba1Sdist 	adt = address((char*)0);
26906fb5685Smark 	if (adt == 0)
27006fb5685Smark 		serror("%s where?|%s requires a trailing address", Command);
27106fb5685Smark 	newline();
27206fb5685Smark 	move1(iscopy, adt);
27306fb5685Smark 	killed();
27406fb5685Smark }
27506fb5685Smark 
27606fb5685Smark move1(cflag, addrt)
27706fb5685Smark 	int cflag;
27806fb5685Smark 	line *addrt;
27906fb5685Smark {
28006fb5685Smark 	register line *adt, *ad1, *ad2;
28106fb5685Smark 	int lines;
28206fb5685Smark 
28306fb5685Smark 	adt = addrt;
28406fb5685Smark 	lines = (addr2 - addr1) + 1;
28506fb5685Smark 	if (cflag) {
28606fb5685Smark 		tad1 = addr1;
28706fb5685Smark 		ad1 = dol;
28806fb5685Smark 		ignore(append(getcopy, ad1++));
28906fb5685Smark 		ad2 = dol;
29006fb5685Smark 	} else {
29106fb5685Smark 		ad2 = addr2;
29206fb5685Smark 		for (ad1 = addr1; ad1 <= ad2;)
29306fb5685Smark 			*ad1++ &= ~01;
29406fb5685Smark 		ad1 = addr1;
29506fb5685Smark 	}
29606fb5685Smark 	ad2++;
29706fb5685Smark 	if (adt < ad1) {
29806fb5685Smark 		if (adt + 1 == ad1 && !cflag && !inglobal)
29906fb5685Smark 			error("That move would do nothing!");
30006fb5685Smark 		dot = adt + (ad2 - ad1);
30106fb5685Smark 		if (++adt != ad1) {
30206fb5685Smark 			reverse(adt, ad1);
30306fb5685Smark 			reverse(ad1, ad2);
30406fb5685Smark 			reverse(adt, ad2);
30506fb5685Smark 		}
30606fb5685Smark 	} else if (adt >= ad2) {
30706fb5685Smark 		dot = adt++;
30806fb5685Smark 		reverse(ad1, ad2);
30906fb5685Smark 		reverse(ad2, adt);
31006fb5685Smark 		reverse(ad1, adt);
31106fb5685Smark 	} else
31206fb5685Smark 		error("Move to a moved line");
31306fb5685Smark 	change();
31406fb5685Smark 	if (!inglobal)
315aecfa433Smark 		if(FIXUNDO) {
31606fb5685Smark 			if (cflag) {
31706fb5685Smark 				undap1 = addrt + 1;
31806fb5685Smark 				undap2 = undap1 + lines;
31906fb5685Smark 				deletenone();
32006fb5685Smark 			} else {
32106fb5685Smark 				undkind = UNDMOVE;
32206fb5685Smark 				undap1 = addr1;
32306fb5685Smark 				undap2 = addr2;
32406fb5685Smark 				unddel = addrt;
32506fb5685Smark 				squish();
32606fb5685Smark 			}
32706fb5685Smark 		}
328aecfa433Smark }
32906fb5685Smark 
33006fb5685Smark getcopy()
33106fb5685Smark {
33206fb5685Smark 
33306fb5685Smark 	if (tad1 > addr2)
33406fb5685Smark 		return (EOF);
33506fb5685Smark 	getline(*tad1++);
33606fb5685Smark 	return (0);
33706fb5685Smark }
33806fb5685Smark 
33906fb5685Smark /*
34006fb5685Smark  * Put lines in the buffer from the undo save area.
34106fb5685Smark  */
34206fb5685Smark getput()
34306fb5685Smark {
34406fb5685Smark 
34506fb5685Smark 	if (tad1 > unddol)
34606fb5685Smark 		return (EOF);
34706fb5685Smark 	getline(*tad1++);
34806fb5685Smark 	tad1++;
34906fb5685Smark 	return (0);
35006fb5685Smark }
35106fb5685Smark 
35206fb5685Smark put()
35306fb5685Smark {
35406fb5685Smark 	register int cnt;
35506fb5685Smark 
356aecfa433Smark 	if (!FIXUNDO)
357aecfa433Smark 		error("Cannot put inside global/macro");
35806fb5685Smark 	cnt = unddol - dol;
35906fb5685Smark 	if (cnt && inopen && pkill[0] && pkill[1]) {
36006fb5685Smark 		pragged(1);
36106fb5685Smark 		return;
36206fb5685Smark 	}
36306fb5685Smark 	tad1 = dol + 1;
36406fb5685Smark 	ignore(append(getput, addr2));
36506fb5685Smark 	undkind = UNDPUT;
36606fb5685Smark 	notecnt = cnt;
36706fb5685Smark 	netchange(cnt);
36806fb5685Smark }
36906fb5685Smark 
37006fb5685Smark /*
37106fb5685Smark  * A tricky put, of a group of lines in the middle
37206fb5685Smark  * of an existing line.  Only from open/visual.
37306fb5685Smark  * Argument says pkills have meaning, e.g. called from
37406fb5685Smark  * put; it is 0 on calls from putreg.
37506fb5685Smark  */
37606fb5685Smark pragged(kill)
37706fb5685Smark 	bool kill;
37806fb5685Smark {
37906fb5685Smark 	extern char *cursor;
38006fb5685Smark 	register char *gp = &genbuf[cursor - linebuf];
38106fb5685Smark 
38206fb5685Smark 	/*
38306fb5685Smark 	 * This kind of stuff is TECO's forte.
38406fb5685Smark 	 * We just grunge along, since it cuts
38506fb5685Smark 	 * across our line-oriented model of the world
38606fb5685Smark 	 * almost scrambling our addled brain.
38706fb5685Smark 	 */
38806fb5685Smark 	if (!kill)
38906fb5685Smark 		getDOT();
39006fb5685Smark 	strcpy(genbuf, linebuf);
39106fb5685Smark 	getline(*unddol);
39206fb5685Smark 	if (kill)
39306fb5685Smark 		*pkill[1] = 0;
39406fb5685Smark 	strcat(linebuf, gp);
39506fb5685Smark 	putmark(unddol);
39606fb5685Smark 	getline(dol[1]);
39706fb5685Smark 	if (kill)
39806fb5685Smark 		strcLIN(pkill[0]);
39906fb5685Smark 	strcpy(gp, linebuf);
40006fb5685Smark 	strcLIN(genbuf);
40106fb5685Smark 	putmark(dol+1);
40206fb5685Smark 	undkind = UNDCHANGE;
40306fb5685Smark 	undap1 = dot;
40406fb5685Smark 	undap2 = dot + 1;
40506fb5685Smark 	unddel = dot - 1;
40606fb5685Smark 	undo(1);
40706fb5685Smark }
40806fb5685Smark 
40906fb5685Smark /*
41006fb5685Smark  * Shift lines, based on c.
41106fb5685Smark  * If c is neither < nor >, then this is a lisp aligning =.
41206fb5685Smark  */
41306fb5685Smark shift(c, cnt)
41406fb5685Smark 	int c;
41506fb5685Smark 	int cnt;
41606fb5685Smark {
41706fb5685Smark 	register line *addr;
41806fb5685Smark 	register char *cp;
41906fb5685Smark 	char *dp;
42006fb5685Smark 	register int i;
42106fb5685Smark 
422aecfa433Smark 	if(FIXUNDO)
42306fb5685Smark 		save12(), undkind = UNDCHANGE;
42406fb5685Smark 	cnt *= value(SHIFTWIDTH);
42506fb5685Smark 	for (addr = addr1; addr <= addr2; addr++) {
42606fb5685Smark 		dot = addr;
42706fb5685Smark #ifdef LISPCODE
42806fb5685Smark 		if (c == '=' && addr == addr1 && addr != addr2)
42906fb5685Smark 			continue;
43006fb5685Smark #endif
43106fb5685Smark 		getDOT();
43206fb5685Smark 		i = whitecnt(linebuf);
43306fb5685Smark 		switch (c) {
43406fb5685Smark 
43506fb5685Smark 		case '>':
43606fb5685Smark 			if (linebuf[0] == 0)
43706fb5685Smark 				continue;
43806fb5685Smark 			cp = genindent(i + cnt);
43906fb5685Smark 			break;
44006fb5685Smark 
44106fb5685Smark 		case '<':
44206fb5685Smark 			if (i == 0)
44306fb5685Smark 				continue;
44406fb5685Smark 			i -= cnt;
44506fb5685Smark 			cp = i > 0 ? genindent(i) : genbuf;
44606fb5685Smark 			break;
44706fb5685Smark 
44806fb5685Smark #ifdef LISPCODE
44906fb5685Smark 		default:
45006fb5685Smark 			i = lindent(addr);
45106fb5685Smark 			getDOT();
45206fb5685Smark 			cp = genindent(i);
45306fb5685Smark 			break;
45406fb5685Smark #endif
45506fb5685Smark 		}
45606fb5685Smark 		if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
45706fb5685Smark 			error("Line too long|Result line after shift would be too long");
45806fb5685Smark 		CP(cp, dp);
45906fb5685Smark 		strcLIN(genbuf);
46006fb5685Smark 		putmark(addr);
46106fb5685Smark 	}
46206fb5685Smark 	killed();
46306fb5685Smark }
46406fb5685Smark 
46506fb5685Smark /*
46606fb5685Smark  * Find a tag in the tags file.
46706fb5685Smark  * Most work here is in parsing the tags file itself.
46806fb5685Smark  */
46906fb5685Smark tagfind(quick)
47006fb5685Smark 	bool quick;
47106fb5685Smark {
47206fb5685Smark 	char cmdbuf[BUFSIZ];
47306fb5685Smark 	char filebuf[FNSIZE];
474aecfa433Smark 	char tagfbuf[128];
47506fb5685Smark 	register int c, d;
47606fb5685Smark 	bool samef = 1;
477aecfa433Smark 	int tfcount = 0;
478aecfa433Smark 	int omagic;
479aecfa433Smark 	char *fn, *fne;
480269e4ba1Sdist 	struct stat sbuf;
481269e4ba1Sdist #ifdef FASTTAG
482269e4ba1Sdist 	int iof;
483269e4ba1Sdist 	char iofbuf[MAXBSIZE];
484aecfa433Smark 	long mid;	/* assumed byte offset */
485aecfa433Smark 	long top, bot;	/* length of tag file */
486aecfa433Smark #endif
48706fb5685Smark 
48806fb5685Smark 	omagic = value(MAGIC);
48906fb5685Smark 	if (!skipend()) {
49006fb5685Smark 		register char *lp = lasttag;
49106fb5685Smark 
49206fb5685Smark 		while (!iswhite(peekchar()) && !endcmd(peekchar()))
49306fb5685Smark 			if (lp < &lasttag[sizeof lasttag - 2])
494c0a5300eSconrad 				*lp++ = ex_getchar();
49506fb5685Smark 			else
49606fb5685Smark 				ignchar();
49706fb5685Smark 		*lp++ = 0;
49806fb5685Smark 		if (!endcmd(peekchar()))
49906fb5685Smark badtag:
50006fb5685Smark 			error("Bad tag|Give one tag per line");
50106fb5685Smark 	} else if (lasttag[0] == 0)
50206fb5685Smark 		error("No previous tag");
503c0a5300eSconrad 	c = ex_getchar();
50406fb5685Smark 	if (!endcmd(c))
50506fb5685Smark 		goto badtag;
50606fb5685Smark 	if (c == EOF)
50706fb5685Smark 		ungetchar(c);
50806fb5685Smark 	clrstats();
509aecfa433Smark 
510aecfa433Smark 	/*
511aecfa433Smark 	 * Loop once for each file in tags "path".
512aecfa433Smark 	 */
513aecfa433Smark 	CP(tagfbuf, svalue(TAGS));
514aecfa433Smark 	fne = tagfbuf - 1;
515aecfa433Smark 	while (fne) {
516aecfa433Smark 		fn = ++fne;
517aecfa433Smark 		while (*fne && *fne != ' ')
518aecfa433Smark 			fne++;
519aecfa433Smark 		if (*fne == 0)
520aecfa433Smark 			fne = 0;	/* done, quit after this time */
521aecfa433Smark 		else
522aecfa433Smark 			*fne = 0;	/* null terminate filename */
523269e4ba1Sdist #ifdef FASTTAG
524269e4ba1Sdist 		iof = topen(fn, iofbuf);
525269e4ba1Sdist 		if (iof == -1)
526aecfa433Smark 			continue;
527aecfa433Smark 		tfcount++;
528269e4ba1Sdist 		fstat(iof, &sbuf);
529aecfa433Smark 		top = sbuf.st_size;
530269e4ba1Sdist 		if (top == 0L )
531aecfa433Smark 			top = -1L;
532aecfa433Smark 		bot = 0L;
533aecfa433Smark 		while (top >= bot) {
534aecfa433Smark #else
535aecfa433Smark 		/*
536aecfa433Smark 		 * Avoid stdio and scan tag file linearly.
537aecfa433Smark 		 */
538aecfa433Smark 		io = open(fn, 0);
539aecfa433Smark 		if (io<0)
540aecfa433Smark 			continue;
541d4cba322Smark 		tfcount++;
542269e4ba1Sdist 		if (fstat(io, &sbuf) < 0)
543269e4ba1Sdist 			bsize = LBSIZE;
544269e4ba1Sdist 		else {
545269e4ba1Sdist 			bsize = sbuf.st_blksize;
546269e4ba1Sdist 			if (bsize <= 0)
547269e4ba1Sdist 				bsize = LBSIZE;
548269e4ba1Sdist 		}
54906fb5685Smark 		while (getfile() == 0) {
550aecfa433Smark #endif
551aecfa433Smark 			/* loop for each tags file entry */
55206fb5685Smark 			register char *cp = linebuf;
55306fb5685Smark 			register char *lp = lasttag;
55406fb5685Smark 			char *oglobp;
55506fb5685Smark 
556269e4ba1Sdist #ifdef FASTTAG
557aecfa433Smark 			mid = (top + bot) / 2;
558269e4ba1Sdist 			tseek(iof, mid);
559aecfa433Smark 			if (mid > 0)	/* to get first tag in file to work */
560269e4ba1Sdist 				/* scan to next \n */
561269e4ba1Sdist 				if(tgets(linebuf, sizeof linebuf, iof)==NULL)
562269e4ba1Sdist 					goto goleft;
563269e4ba1Sdist 			/* get the line itself */
564269e4ba1Sdist 			if(tgets(linebuf, sizeof linebuf, iof)==NULL)
565269e4ba1Sdist 				goto goleft;
566269e4ba1Sdist #ifdef TDEBUG
567c0a5300eSconrad 			ex_printf("tag: %o %o %o %s\n", bot, mid, top, linebuf);
568269e4ba1Sdist #endif
569aecfa433Smark #endif
57006fb5685Smark 			while (*cp && *lp == *cp)
57106fb5685Smark 				cp++, lp++;
572269e4ba1Sdist 			if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 ||
573269e4ba1Sdist 			    lp-lasttag < value(TAGLENGTH))) {
574269e4ba1Sdist #ifdef FASTTAG
575aecfa433Smark 				if (*lp > *cp)
576aecfa433Smark 					bot = mid + 1;
577aecfa433Smark 				else
578269e4ba1Sdist goleft:
579aecfa433Smark 					top = mid - 1;
580aecfa433Smark #endif
581aecfa433Smark 				/* Not this tag.  Try the next */
58206fb5685Smark 				continue;
583aecfa433Smark 			}
584aecfa433Smark 
585aecfa433Smark 			/*
586aecfa433Smark 			 * We found the tag.  Decode the line in the file.
587aecfa433Smark 			 */
588269e4ba1Sdist #ifdef FASTTAG
589269e4ba1Sdist 			tclose(iof);
590aecfa433Smark #else
59106fb5685Smark 			close(io);
592aecfa433Smark #endif
593d4cba322Smark 			/* Rest of tag if abbreviated */
594d4cba322Smark 			while (!iswhite(*cp))
595d4cba322Smark 				cp++;
596d4cba322Smark 
597aecfa433Smark 			/* name of file */
59806fb5685Smark 			while (*cp && iswhite(*cp))
59906fb5685Smark 				cp++;
60006fb5685Smark 			if (!*cp)
60106fb5685Smark badtags:
60206fb5685Smark 				serror("%s: Bad tags file entry", lasttag);
60306fb5685Smark 			lp = filebuf;
60406fb5685Smark 			while (*cp && *cp != ' ' && *cp != '\t') {
60506fb5685Smark 				if (lp < &filebuf[sizeof filebuf - 2])
60606fb5685Smark 					*lp++ = *cp;
60706fb5685Smark 				cp++;
60806fb5685Smark 			}
60906fb5685Smark 			*lp++ = 0;
610aecfa433Smark 
61106fb5685Smark 			if (*cp == 0)
61206fb5685Smark 				goto badtags;
61306fb5685Smark 			if (dol != zero) {
61406fb5685Smark 				/*
61506fb5685Smark 				 * Save current position in 't for ^^ in visual.
61606fb5685Smark 				 */
61706fb5685Smark 				names['t'-'a'] = *dot &~ 01;
61806fb5685Smark 				if (inopen) {
619d4cba322Smark 					extern char *ncols['z'-'a'+2];
62006fb5685Smark 					extern char *cursor;
62106fb5685Smark 
62206fb5685Smark 					ncols['t'-'a'] = cursor;
62306fb5685Smark 				}
62406fb5685Smark 			}
62506fb5685Smark 			strcpy(cmdbuf, cp);
62606fb5685Smark 			if (strcmp(filebuf, savedfile) || !edited) {
62706fb5685Smark 				char cmdbuf2[sizeof filebuf + 10];
62806fb5685Smark 
629aecfa433Smark 				/* Different file.  Do autowrite & get it. */
63006fb5685Smark 				if (!quick) {
63106fb5685Smark 					ckaw();
63206fb5685Smark 					if (chng && dol > zero)
63306fb5685Smark 						error("No write@since last change (:tag! overrides)");
63406fb5685Smark 				}
63506fb5685Smark 				oglobp = globp;
63606fb5685Smark 				strcpy(cmdbuf2, "e! ");
63706fb5685Smark 				strcat(cmdbuf2, filebuf);
63806fb5685Smark 				globp = cmdbuf2;
63906fb5685Smark 				d = peekc; ungetchar(0);
640aecfa433Smark 				commands(1, 1);
641aecfa433Smark 				peekc = d;
642aecfa433Smark 				globp = oglobp;
643aecfa433Smark 				value(MAGIC) = omagic;
644aecfa433Smark 				samef = 0;
645aecfa433Smark 			}
646aecfa433Smark 
647aecfa433Smark 			/*
648aecfa433Smark 			 * Look for pattern in the current file.
649aecfa433Smark 			 */
650aecfa433Smark 			oglobp = globp;
651aecfa433Smark 			globp = cmdbuf;
652aecfa433Smark 			d = peekc; ungetchar(0);
653aecfa433Smark 			if (samef)
654aecfa433Smark 				markpr(dot);
655f80bacf1Smark 			/*
656f80bacf1Smark 			 * BUG: if it isn't found (user edited header
657f80bacf1Smark 			 * line) we get left in nomagic mode.
658f80bacf1Smark 			 */
659f80bacf1Smark 			value(MAGIC) = 0;
66006fb5685Smark 			commands(1, 1);
66106fb5685Smark 			peekc = d;
66206fb5685Smark 			globp = oglobp;
663f80bacf1Smark 			value(MAGIC) = omagic;
66406fb5685Smark 			return;
665aecfa433Smark 		}	/* end of "for each tag in file" */
666aecfa433Smark 
667aecfa433Smark 		/*
668aecfa433Smark 		 * No such tag in this file.  Close it and try the next.
669aecfa433Smark 		 */
670269e4ba1Sdist #ifdef FASTTAG
671269e4ba1Sdist 		tclose(iof);
672aecfa433Smark #else
673aecfa433Smark 		close(io);
674aecfa433Smark #endif
675aecfa433Smark 	}	/* end of "for each file in path" */
676aecfa433Smark 	if (tfcount <= 0)
67706fb5685Smark 		error("No tags file");
678aecfa433Smark 	else
67906fb5685Smark 		serror("%s: No such tag@in tags file", lasttag);
68006fb5685Smark }
68106fb5685Smark 
68206fb5685Smark /*
68306fb5685Smark  * Save lines from addr1 thru addr2 as though
68406fb5685Smark  * they had been deleted.
68506fb5685Smark  */
68606fb5685Smark yank()
68706fb5685Smark {
68806fb5685Smark 
689aecfa433Smark 	if (!FIXUNDO)
690aecfa433Smark 		error("Can't yank inside global/macro");
69106fb5685Smark 	save12();
69206fb5685Smark 	undkind = UNDNONE;
69306fb5685Smark 	killcnt(addr2 - addr1 + 1);
69406fb5685Smark }
69506fb5685Smark 
69606fb5685Smark /*
69706fb5685Smark  * z command; print windows of text in the file.
69806fb5685Smark  *
69906fb5685Smark  * If this seems unreasonably arcane, the reasons
70006fb5685Smark  * are historical.  This is one of the first commands
70106fb5685Smark  * added to the first ex (then called en) and the
70206fb5685Smark  * number of facilities here were the major advantage
70306fb5685Smark  * of en over ed since they allowed more use to be
70406fb5685Smark  * made of fast terminals w/o typing .,.22p all the time.
70506fb5685Smark  */
70606fb5685Smark bool	zhadpr;
70706fb5685Smark bool	znoclear;
70806fb5685Smark short	zweight;
70906fb5685Smark 
71006fb5685Smark zop(hadpr)
71106fb5685Smark 	int hadpr;
71206fb5685Smark {
71306fb5685Smark 	register int c, lines, op;
71406fb5685Smark 	bool excl;
71506fb5685Smark 
71606fb5685Smark 	zhadpr = hadpr;
71706fb5685Smark 	notempty();
71806fb5685Smark 	znoclear = 0;
71906fb5685Smark 	zweight = 0;
72006fb5685Smark 	excl = exclam();
721c0a5300eSconrad 	switch (c = op = ex_getchar()) {
72206fb5685Smark 
72306fb5685Smark 	case '^':
72406fb5685Smark 		zweight = 1;
72506fb5685Smark 	case '-':
72606fb5685Smark 	case '+':
72706fb5685Smark 		while (peekchar() == op) {
72806fb5685Smark 			ignchar();
72906fb5685Smark 			zweight++;
73006fb5685Smark 		}
73106fb5685Smark 	case '=':
73206fb5685Smark 	case '.':
733c0a5300eSconrad 		c = ex_getchar();
73406fb5685Smark 		break;
73506fb5685Smark 
73606fb5685Smark 	case EOF:
73706fb5685Smark 		znoclear++;
73806fb5685Smark 		break;
73906fb5685Smark 
74006fb5685Smark 	default:
74106fb5685Smark 		op = 0;
74206fb5685Smark 		break;
74306fb5685Smark 	}
74406fb5685Smark 	if (isdigit(c)) {
74506fb5685Smark 		lines = c - '0';
74606fb5685Smark 		for(;;) {
747c0a5300eSconrad 			c = ex_getchar();
74806fb5685Smark 			if (!isdigit(c))
74906fb5685Smark 				break;
75006fb5685Smark 			lines *= 10;
75106fb5685Smark 			lines += c - '0';
75206fb5685Smark 		}
75306fb5685Smark 		if (lines < LINES)
75406fb5685Smark 			znoclear++;
75506fb5685Smark 		value(WINDOW) = lines;
75606fb5685Smark 		if (op == '=')
75706fb5685Smark 			lines += 2;
75806fb5685Smark 	} else
75906fb5685Smark 		lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL);
76006fb5685Smark 	if (inopen || c != EOF) {
76106fb5685Smark 		ungetchar(c);
76206fb5685Smark 		newline();
76306fb5685Smark 	}
76406fb5685Smark 	addr1 = addr2;
76506fb5685Smark 	if (addr2 == 0 && dot < dol && op == 0)
76606fb5685Smark 		addr1 = addr2 = dot+1;
76706fb5685Smark 	setdot();
76806fb5685Smark 	zop2(lines, op);
76906fb5685Smark }
77006fb5685Smark 
77106fb5685Smark zop2(lines, op)
77206fb5685Smark 	register int lines;
77306fb5685Smark 	register int op;
77406fb5685Smark {
77506fb5685Smark 	register line *split;
77606fb5685Smark 
77706fb5685Smark 	split = NULL;
77806fb5685Smark 	switch (op) {
77906fb5685Smark 
78006fb5685Smark 	case EOF:
78106fb5685Smark 		if (addr2 == dol)
78206fb5685Smark 			error("\nAt EOF");
78306fb5685Smark 	case '+':
78406fb5685Smark 		if (addr2 == dol)
78506fb5685Smark 			error("At EOF");
78606fb5685Smark 		addr2 += lines * zweight;
78706fb5685Smark 		if (addr2 > dol)
78806fb5685Smark 			error("Hit BOTTOM");
78906fb5685Smark 		addr2++;
79006fb5685Smark 	default:
79106fb5685Smark 		addr1 = addr2;
79206fb5685Smark 		addr2 += lines-1;
79306fb5685Smark 		dot = addr2;
79406fb5685Smark 		break;
79506fb5685Smark 
79606fb5685Smark 	case '=':
79706fb5685Smark 	case '.':
79806fb5685Smark 		znoclear = 0;
79906fb5685Smark 		lines--;
80006fb5685Smark 		lines >>= 1;
80106fb5685Smark 		if (op == '=')
80206fb5685Smark 			lines--;
80306fb5685Smark 		addr1 = addr2 - lines;
80406fb5685Smark 		if (op == '=')
80506fb5685Smark 			dot = split = addr2;
80606fb5685Smark 		addr2 += lines;
80706fb5685Smark 		if (op == '.') {
80806fb5685Smark 			markDOT();
80906fb5685Smark 			dot = addr2;
81006fb5685Smark 		}
81106fb5685Smark 		break;
81206fb5685Smark 
81306fb5685Smark 	case '^':
81406fb5685Smark 	case '-':
81506fb5685Smark 		addr2 -= lines * zweight;
81606fb5685Smark 		if (addr2 < one)
81706fb5685Smark 			error("Hit TOP");
81806fb5685Smark 		lines--;
81906fb5685Smark 		addr1 = addr2 - lines;
82006fb5685Smark 		dot = addr2;
82106fb5685Smark 		break;
82206fb5685Smark 	}
82306fb5685Smark 	if (addr1 <= zero)
82406fb5685Smark 		addr1 = one;
82506fb5685Smark 	if (addr2 > dol)
82606fb5685Smark 		addr2 = dol;
82706fb5685Smark 	if (dot > dol)
82806fb5685Smark 		dot = dol;
82906fb5685Smark 	if (addr1 > addr2)
83006fb5685Smark 		return;
83106fb5685Smark 	if (op == EOF && zhadpr) {
83206fb5685Smark 		getline(*addr1);
833c0a5300eSconrad 		ex_putchar('\r' | QUOTE);
83406fb5685Smark 		shudclob = 1;
83506fb5685Smark 	} else if (znoclear == 0 && CL != NOSTR && !inopen) {
83606fb5685Smark 		flush1();
83706fb5685Smark 		vclear();
83806fb5685Smark 	}
83906fb5685Smark 	if (addr2 - addr1 > 1)
84006fb5685Smark 		pstart();
84106fb5685Smark 	if (split) {
84206fb5685Smark 		plines(addr1, split - 1, 0);
84306fb5685Smark 		splitit();
84406fb5685Smark 		plines(split, split, 0);
84506fb5685Smark 		splitit();
84606fb5685Smark 		addr1 = split + 1;
84706fb5685Smark 	}
84806fb5685Smark 	plines(addr1, addr2, 0);
84906fb5685Smark }
85006fb5685Smark 
85106fb5685Smark static
85206fb5685Smark splitit()
85306fb5685Smark {
85406fb5685Smark 	register int l;
85506fb5685Smark 
85606fb5685Smark 	for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
857c0a5300eSconrad 		ex_putchar('-');
85806fb5685Smark 	putnl();
85906fb5685Smark }
86006fb5685Smark 
86106fb5685Smark plines(adr1, adr2, movedot)
86206fb5685Smark 	line *adr1;
86306fb5685Smark 	register line *adr2;
86406fb5685Smark 	bool movedot;
86506fb5685Smark {
86606fb5685Smark 	register line *addr;
86706fb5685Smark 
86806fb5685Smark 	pofix();
86906fb5685Smark 	for (addr = adr1; addr <= adr2; addr++) {
87006fb5685Smark 		getline(*addr);
87106fb5685Smark 		pline(lineno(addr));
87206fb5685Smark 		if (inopen)
873c0a5300eSconrad 			ex_putchar('\n' | QUOTE);
87406fb5685Smark 		if (movedot)
87506fb5685Smark 			dot = addr;
87606fb5685Smark 	}
87706fb5685Smark }
87806fb5685Smark 
87906fb5685Smark pofix()
88006fb5685Smark {
88106fb5685Smark 
88206fb5685Smark 	if (inopen && Outchar != termchar) {
88306fb5685Smark 		vnfl();
88406fb5685Smark 		setoutt();
88506fb5685Smark 	}
88606fb5685Smark }
88706fb5685Smark 
88806fb5685Smark /*
88906fb5685Smark  * Dudley doright to the rescue.
89006fb5685Smark  * Undo saves the day again.
89106fb5685Smark  * A tip of the hatlo hat to Warren Teitleman
89206fb5685Smark  * who made undo as useful as do.
89306fb5685Smark  *
89406fb5685Smark  * Command level undo works easily because
89506fb5685Smark  * the editor has a unique temporary file
89606fb5685Smark  * index for every line which ever existed.
89706fb5685Smark  * We don't have to save large blocks of text,
89806fb5685Smark  * only the indices which are small.  We do this
89906fb5685Smark  * by moving them to after the last line in the
90006fb5685Smark  * line buffer array, and marking down info
90106fb5685Smark  * about whence they came.
90206fb5685Smark  *
90306fb5685Smark  * Undo is its own inverse.
90406fb5685Smark  */
90506fb5685Smark undo(c)
90606fb5685Smark 	bool c;
90706fb5685Smark {
90806fb5685Smark 	register int i;
90906fb5685Smark 	register line *jp, *kp;
91006fb5685Smark 	line *dolp1, *newdol, *newadot;
91106fb5685Smark 
912aecfa433Smark #ifdef TRACE
913aecfa433Smark 	if (trace)
914aecfa433Smark 		vudump("before undo");
915aecfa433Smark #endif
91606fb5685Smark 	if (inglobal && inopen <= 0)
91706fb5685Smark 		error("Can't undo in global@commands");
91806fb5685Smark 	if (!c)
91906fb5685Smark 		somechange();
92006fb5685Smark 	pkill[0] = pkill[1] = 0;
92106fb5685Smark 	change();
92206fb5685Smark 	if (undkind == UNDMOVE) {
92306fb5685Smark  		/*
92406fb5685Smark 		 * Command to be undone is a move command.
92506fb5685Smark 		 * This is handled as a special case by noting that
92606fb5685Smark 		 * a move "a,b m c" can be inverted by another move.
92706fb5685Smark 		 */
92806fb5685Smark 		if ((i = (jp = unddel) - undap2) > 0) {
92906fb5685Smark 			/*
93006fb5685Smark 			 * when c > b inverse is a+(c-b),c m a-1
93106fb5685Smark 			 */
93206fb5685Smark 			addr2 = jp;
93306fb5685Smark 			addr1 = (jp = undap1) + i;
93406fb5685Smark 			unddel = jp-1;
93506fb5685Smark 		} else {
93606fb5685Smark 			/*
93706fb5685Smark 			 * when b > c inverse is  c+1,c+1+(b-a) m b
93806fb5685Smark 			 */
93906fb5685Smark 			addr1 = ++jp;
94006fb5685Smark 			addr2 = jp + ((unddel = undap2) - undap1);
94106fb5685Smark 		}
94206fb5685Smark 		kp = undap1;
94306fb5685Smark 		move1(0, unddel);
94406fb5685Smark 		dot = kp;
94506fb5685Smark 		Command = "move";
94606fb5685Smark 		killed();
94706fb5685Smark 	} else {
94806fb5685Smark 		int cnt;
94906fb5685Smark 
95006fb5685Smark 		newadot = dot;
95106fb5685Smark 		cnt = lineDOL();
95206fb5685Smark 		newdol = dol;
95306fb5685Smark 		dolp1 = dol + 1;
95406fb5685Smark 		/*
95506fb5685Smark 		 * Command to be undone is a non-move.
95606fb5685Smark 		 * All such commands are treated as a combination of
95706fb5685Smark 		 * a delete command and a append command.
95806fb5685Smark 		 * We first move the lines appended by the last command
95906fb5685Smark 		 * from undap1 to undap2-1 so that they are just before the
96006fb5685Smark 		 * saved deleted lines.
96106fb5685Smark 		 */
96206fb5685Smark 		if ((i = (kp = undap2) - (jp = undap1)) > 0) {
96306fb5685Smark 			if (kp != dolp1) {
96406fb5685Smark 				reverse(jp, kp);
96506fb5685Smark 				reverse(kp, dolp1);
96606fb5685Smark 				reverse(jp, dolp1);
96706fb5685Smark 			}
96806fb5685Smark 			/*
96906fb5685Smark 			 * Account for possible backward motion of target
97006fb5685Smark 			 * for restoration of saved deleted lines.
97106fb5685Smark 			 */
97206fb5685Smark 			if (unddel >= jp)
97306fb5685Smark 				unddel -= i;
97406fb5685Smark 			newdol -= i;
97506fb5685Smark 			/*
97606fb5685Smark 			 * For the case where no lines are restored, dot
97706fb5685Smark 			 * is the line before the first line deleted.
97806fb5685Smark 			 */
97906fb5685Smark 			dot = jp-1;
98006fb5685Smark 		}
98106fb5685Smark 		/*
98206fb5685Smark 		 * Now put the deleted lines, if any, back where they were.
98306fb5685Smark 		 * Basic operation is: dol+1,unddol m unddel
98406fb5685Smark 		 */
98506fb5685Smark 		if (undkind == UNDPUT) {
98606fb5685Smark 			unddel = undap1 - 1;
98706fb5685Smark 			squish();
98806fb5685Smark 		}
98906fb5685Smark 		jp = unddel + 1;
99006fb5685Smark 		if ((i = (kp = unddol) - dol) > 0) {
99106fb5685Smark 			if (jp != dolp1) {
99206fb5685Smark 				reverse(jp, dolp1);
99306fb5685Smark 				reverse(dolp1, ++kp);
99406fb5685Smark 				reverse(jp, kp);
99506fb5685Smark 			}
99606fb5685Smark 			/*
99706fb5685Smark 			 * Account for possible forward motion of the target
99806fb5685Smark 			 * for restoration of the deleted lines.
99906fb5685Smark 			 */
100006fb5685Smark 			if (undap1 >= jp)
100106fb5685Smark 				undap1 += i;
100206fb5685Smark 			/*
100306fb5685Smark 			 * Dot is the first resurrected line.
100406fb5685Smark 			 */
100506fb5685Smark 			dot = jp;
100606fb5685Smark 			newdol += i;
100706fb5685Smark 		}
100806fb5685Smark 		/*
100906fb5685Smark 		 * Clean up so we are invertible
101006fb5685Smark 		 */
101106fb5685Smark 		unddel = undap1 - 1;
101206fb5685Smark 		undap1 = jp;
101306fb5685Smark 		undap2 = jp + i;
101406fb5685Smark 		dol = newdol;
101506fb5685Smark 		netchHAD(cnt);
101606fb5685Smark 		if (undkind == UNDALL) {
101706fb5685Smark 			dot = undadot;
101806fb5685Smark 			undadot = newadot;
1019aecfa433Smark 		} else
102006fb5685Smark 			undkind = UNDCHANGE;
102106fb5685Smark 	}
1022d4cba322Smark 	/*
1023d4cba322Smark 	 * Defensive programming - after a munged undadot.
1024d4cba322Smark 	 * Also handle empty buffer case.
1025d4cba322Smark 	 */
1026d4cba322Smark 	if ((dot <= zero || dot > dol) && dot != dol)
102706fb5685Smark 		dot = one;
1028aecfa433Smark #ifdef TRACE
1029aecfa433Smark 	if (trace)
1030aecfa433Smark 		vudump("after undo");
1031aecfa433Smark #endif
103206fb5685Smark }
103306fb5685Smark 
103406fb5685Smark /*
103506fb5685Smark  * Be (almost completely) sure there really
103606fb5685Smark  * was a change, before claiming to undo.
103706fb5685Smark  */
103806fb5685Smark somechange()
103906fb5685Smark {
104006fb5685Smark 	register line *ip, *jp;
104106fb5685Smark 
104206fb5685Smark 	switch (undkind) {
104306fb5685Smark 
104406fb5685Smark 	case UNDMOVE:
104506fb5685Smark 		return;
104606fb5685Smark 
104706fb5685Smark 	case UNDCHANGE:
104806fb5685Smark 		if (undap1 == undap2 && dol == unddol)
104906fb5685Smark 			break;
105006fb5685Smark 		return;
105106fb5685Smark 
105206fb5685Smark 	case UNDPUT:
105306fb5685Smark 		if (undap1 != undap2)
105406fb5685Smark 			return;
105506fb5685Smark 		break;
105606fb5685Smark 
105706fb5685Smark 	case UNDALL:
105806fb5685Smark 		if (unddol - dol != lineDOL())
105906fb5685Smark 			return;
106006fb5685Smark 		for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
106106fb5685Smark 			if ((*ip &~ 01) != (*jp &~ 01))
106206fb5685Smark 				return;
106306fb5685Smark 		break;
106406fb5685Smark 
106506fb5685Smark 	case UNDNONE:
106606fb5685Smark 		error("Nothing to undo");
106706fb5685Smark 	}
106806fb5685Smark 	error("Nothing changed|Last undoable command didn't change anything");
106906fb5685Smark }
107006fb5685Smark 
107106fb5685Smark /*
107206fb5685Smark  * Map command:
107306fb5685Smark  * map src dest
107406fb5685Smark  */
1075d4cba322Smark mapcmd(un, ab)
107606fb5685Smark 	int un;	/* true if this is unmap command */
1077d4cba322Smark 	int ab;	/* true if this is abbr command */
107806fb5685Smark {
1079d4cba322Smark 	char lhs[100], rhs[100];	/* max sizes resp. */
108006fb5685Smark 	register char *p;
1081269e4ba1Sdist 	register int c;		/* mjm: char --> int */
108206fb5685Smark 	char *dname;
1083aecfa433Smark 	struct maps *mp;	/* the map structure we are working on */
108406fb5685Smark 
1085d4cba322Smark 	mp = ab ? abbrevs : exclam() ? immacs : arrows;
108606fb5685Smark 	if (skipend()) {
108706fb5685Smark 		int i;
108806fb5685Smark 
108906fb5685Smark 		/* print current mapping values */
109006fb5685Smark 		if (peekchar() != EOF)
109106fb5685Smark 			ignchar();
1092d4cba322Smark 		if (un)
1093d4cba322Smark 			error("Missing lhs");
109406fb5685Smark 		if (inopen)
109506fb5685Smark 			pofix();
1096aecfa433Smark 		for (i=0; mp[i].mapto; i++)
1097aecfa433Smark 			if (mp[i].cap) {
1098aecfa433Smark 				lprintf("%s", mp[i].descr);
1099c0a5300eSconrad 				ex_putchar('\t');
1100aecfa433Smark 				lprintf("%s", mp[i].cap);
1101c0a5300eSconrad 				ex_putchar('\t');
1102aecfa433Smark 				lprintf("%s", mp[i].mapto);
110306fb5685Smark 				putNFL();
110406fb5685Smark 			}
110506fb5685Smark 		return;
110606fb5685Smark 	}
110706fb5685Smark 
110806fb5685Smark 	ignore(skipwh());
110906fb5685Smark 	for (p=lhs; ; ) {
1110c0a5300eSconrad 		c = ex_getchar();
1111*2949201cSbostic 		if (c == CTRL('v')) {
1112c0a5300eSconrad 			c = ex_getchar();
1113d4cba322Smark 		} else if (!un && any(c, " \t")) {
1114d4cba322Smark 			/* End of lhs */
111506fb5685Smark 			break;
1116d4cba322Smark 		} else if (endcmd(c) && c!='"') {
111706fb5685Smark 			ungetchar(c);
111806fb5685Smark 			if (un) {
111906fb5685Smark 				newline();
1120d4cba322Smark 				*p = 0;
1121aecfa433Smark 				addmac(lhs, NOSTR, NOSTR, mp);
112206fb5685Smark 				return;
112306fb5685Smark 			} else
112406fb5685Smark 				error("Missing rhs");
112506fb5685Smark 		}
112606fb5685Smark 		*p++ = c;
112706fb5685Smark 	}
112806fb5685Smark 	*p = 0;
112906fb5685Smark 
113006fb5685Smark 	if (skipend())
113106fb5685Smark 		error("Missing rhs");
113206fb5685Smark 	for (p=rhs; ; ) {
1133c0a5300eSconrad 		c = ex_getchar();
1134*2949201cSbostic 		if (c == CTRL('v')) {
1135c0a5300eSconrad 			c = ex_getchar();
1136d4cba322Smark 		} else if (endcmd(c) && c!='"') {
113706fb5685Smark 			ungetchar(c);
113806fb5685Smark 			break;
113906fb5685Smark 		}
114006fb5685Smark 		*p++ = c;
114106fb5685Smark 	}
114206fb5685Smark 	*p = 0;
114306fb5685Smark 	newline();
114406fb5685Smark 	/*
114506fb5685Smark 	 * Special hack for function keys: #1 means key f1, etc.
114606fb5685Smark 	 * If the terminal doesn't have function keys, we just use #1.
114706fb5685Smark 	 */
114806fb5685Smark 	if (lhs[0] == '#') {
114906fb5685Smark 		char *fnkey;
115006fb5685Smark 		char *fkey();
115106fb5685Smark 		char funkey[3];
115206fb5685Smark 
115306fb5685Smark 		fnkey = fkey(lhs[1] - '0');
115406fb5685Smark 		funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
115506fb5685Smark 		if (fnkey)
115606fb5685Smark 			strcpy(lhs, fnkey);
115706fb5685Smark 		dname = funkey;
115806fb5685Smark 	} else {
115906fb5685Smark 		dname = lhs;
116006fb5685Smark 	}
1161aecfa433Smark 	addmac(lhs,rhs,dname,mp);
116206fb5685Smark }
116306fb5685Smark 
116406fb5685Smark /*
116506fb5685Smark  * Add a macro definition to those that already exist. The sequence of
116606fb5685Smark  * chars "src" is mapped into "dest". If src is already mapped into something
116706fb5685Smark  * this overrides the mapping. There is no recursion. Unmap is done by
1168aecfa433Smark  * using NOSTR for dest.  Dname is what to show in listings.  mp is
1169aecfa433Smark  * the structure to affect (arrows, etc).
117006fb5685Smark  */
1171aecfa433Smark addmac(src,dest,dname,mp)
117206fb5685Smark 	register char *src, *dest, *dname;
1173aecfa433Smark 	register struct maps *mp;
117406fb5685Smark {
117506fb5685Smark 	register int slot, zer;
117606fb5685Smark 
1177d4cba322Smark #ifdef TRACE
1178d4cba322Smark 	if (trace)
1179d4cba322Smark 		fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
1180d4cba322Smark #endif
1181aecfa433Smark 	if (dest && mp==arrows) {
118206fb5685Smark 		/* Make sure user doesn't screw himself */
118306fb5685Smark 		/*
1184f80bacf1Smark 		 * Prevent tail recursion. We really should be
1185f80bacf1Smark 		 * checking to see if src is a suffix of dest
1186aecfa433Smark 		 * but this makes mapping involving escapes that
1187aecfa433Smark 		 * is reasonable mess up.
118806fb5685Smark 		 */
1189f80bacf1Smark 		if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
1190f80bacf1Smark 			error("No tail recursion");
119106fb5685Smark 		/*
119206fb5685Smark 		 * We don't let the user rob himself of ":", and making
119306fb5685Smark 		 * multi char words is a bad idea so we don't allow it.
119406fb5685Smark 		 * Note that if user sets mapinput and maps all of return,
119506fb5685Smark 		 * linefeed, and escape, he can screw himself. This is
119606fb5685Smark 		 * so weird I don't bother to check for it.
119706fb5685Smark 		 */
119806fb5685Smark 		if (isalpha(src[0]) && src[1] || any(src[0],":"))
119906fb5685Smark 			error("Too dangerous to map that");
1200aecfa433Smark 	}
1201aecfa433Smark 	else if (dest) {
1202aecfa433Smark 		/* check for tail recursion in input mode: fussier */
1203aecfa433Smark 		if (eq(src, dest+strlen(dest)-strlen(src)))
1204aecfa433Smark 			error("No tail recursion");
1205aecfa433Smark 	}
120606fb5685Smark 	/*
120706fb5685Smark 	 * If the src were null it would cause the dest to
120806fb5685Smark 	 * be mapped always forever. This is not good.
120906fb5685Smark 	 */
1210aecfa433Smark 	if (src == NOSTR || src[0] == 0)
1211aecfa433Smark 		error("Missing lhs");
121206fb5685Smark 
121306fb5685Smark 	/* see if we already have a def for src */
121406fb5685Smark 	zer = -1;
1215aecfa433Smark 	for (slot=0; mp[slot].mapto; slot++) {
1216aecfa433Smark 		if (mp[slot].cap) {
1217d4cba322Smark 			if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
121806fb5685Smark 				break;	/* if so, reuse slot */
121906fb5685Smark 		} else {
122006fb5685Smark 			zer = slot;	/* remember an empty slot */
122106fb5685Smark 		}
122206fb5685Smark 	}
122306fb5685Smark 
122406fb5685Smark 	if (dest == NOSTR) {
122506fb5685Smark 		/* unmap */
1226aecfa433Smark 		if (mp[slot].cap) {
1227aecfa433Smark 			mp[slot].cap = NOSTR;
1228aecfa433Smark 			mp[slot].descr = NOSTR;
122906fb5685Smark 		} else {
123006fb5685Smark 			error("Not mapped|That macro wasn't mapped");
123106fb5685Smark 		}
123206fb5685Smark 		return;
123306fb5685Smark 	}
123406fb5685Smark 
123506fb5685Smark 	/* reuse empty slot, if we found one and src isn't already defined */
1236aecfa433Smark 	if (zer >= 0 && mp[slot].mapto == 0)
123706fb5685Smark 		slot = zer;
123806fb5685Smark 
123906fb5685Smark 	/* if not, append to end */
124006fb5685Smark 	if (slot >= MAXNOMACS)
124106fb5685Smark 		error("Too many macros");
124206fb5685Smark 	if (msnext == 0)	/* first time */
124306fb5685Smark 		msnext = mapspace;
124406fb5685Smark 	/* Check is a bit conservative, we charge for dname even if reusing src */
124506fb5685Smark 	if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS)
124606fb5685Smark 		error("Too much macro text");
124706fb5685Smark 	CP(msnext, src);
1248aecfa433Smark 	mp[slot].cap = msnext;
124906fb5685Smark 	msnext += strlen(src) + 1;	/* plus 1 for null on the end */
125006fb5685Smark 	CP(msnext, dest);
1251aecfa433Smark 	mp[slot].mapto = msnext;
125206fb5685Smark 	msnext += strlen(dest) + 1;
125306fb5685Smark 	if (dname) {
125406fb5685Smark 		CP(msnext, dname);
1255aecfa433Smark 		mp[slot].descr = msnext;
125606fb5685Smark 		msnext += strlen(dname) + 1;
125706fb5685Smark 	} else {
125806fb5685Smark 		/* default descr to string user enters */
1259aecfa433Smark 		mp[slot].descr = src;
126006fb5685Smark 	}
126106fb5685Smark }
126206fb5685Smark 
126306fb5685Smark /*
126406fb5685Smark  * Implements macros from command mode. c is the buffer to
126506fb5685Smark  * get the macro from.
126606fb5685Smark  */
126706fb5685Smark cmdmac(c)
126806fb5685Smark char c;
126906fb5685Smark {
127006fb5685Smark 	char macbuf[BUFSIZ];
127106fb5685Smark 	line *ad, *a1, *a2;
127206fb5685Smark 	char *oglobp;
1273269e4ba1Sdist 	short pk;
127406fb5685Smark 	bool oinglobal;
127506fb5685Smark 
127606fb5685Smark 	lastmac = c;
127706fb5685Smark 	oglobp = globp;
127806fb5685Smark 	oinglobal = inglobal;
127906fb5685Smark 	pk = peekc; peekc = 0;
128006fb5685Smark 	if (inglobal < 2)
128106fb5685Smark 		inglobal = 1;
128206fb5685Smark 	regbuf(c, macbuf, sizeof(macbuf));
128306fb5685Smark 	a1 = addr1; a2 = addr2;
128406fb5685Smark 	for (ad=a1; ad<=a2; ad++) {
128506fb5685Smark 		globp = macbuf;
128606fb5685Smark 		dot = ad;
128706fb5685Smark 		commands(1,1);
128806fb5685Smark 	}
128906fb5685Smark 	globp = oglobp;
129006fb5685Smark 	inglobal = oinglobal;
129106fb5685Smark 	peekc = pk;
129206fb5685Smark }
1293