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