xref: /original-bsd/usr.bin/ex/ex_cmdsub.c (revision d4cba322)
106fb5685Smark /* Copyright (c) 1979 Regents of the University of California */
206fb5685Smark #include "ex.h"
306fb5685Smark #include "ex_argv.h"
406fb5685Smark #include "ex_temp.h"
506fb5685Smark #include "ex_tty.h"
6*d4cba322Smark #include "ex_vis.h"
706fb5685Smark 
806fb5685Smark /*
906fb5685Smark  * Command mode subroutines implementing
1006fb5685Smark  *	append, args, copy, delete, join, move, put,
1106fb5685Smark  *	shift, tag, yank, z and undo
1206fb5685Smark  */
1306fb5685Smark 
1406fb5685Smark bool	endline = 1;
1506fb5685Smark line	*tad1;
16*d4cba322Smark static	jnoop();
1706fb5685Smark 
1806fb5685Smark /*
1906fb5685Smark  * Append after line a lines returned by function f.
2006fb5685Smark  * Be careful about intermediate states to avoid scramble
2106fb5685Smark  * if an interrupt comes in.
2206fb5685Smark  */
2306fb5685Smark append(f, a)
2406fb5685Smark 	int (*f)();
2506fb5685Smark 	line *a;
2606fb5685Smark {
2706fb5685Smark 	register line *a1, *a2, *rdot;
2806fb5685Smark 	int nline;
2906fb5685Smark 
3006fb5685Smark 	nline = 0;
3106fb5685Smark 	dot = a;
32aecfa433Smark 	if(FIXUNDO && !inopen && f!=getsub) {
3306fb5685Smark 		undap1 = undap2 = dot + 1;
3406fb5685Smark 		undkind = UNDCHANGE;
3506fb5685Smark 	}
3606fb5685Smark 	while ((*f)() == 0) {
3706fb5685Smark 		if (truedol >= endcore) {
3806fb5685Smark 			if (morelines() < 0) {
39aecfa433Smark 				if (FIXUNDO && f == getsub) {
4006fb5685Smark 					undap1 = addr1;
4106fb5685Smark 					undap2 = addr2 + 1;
4206fb5685Smark 				}
4306fb5685Smark 				error("Out of memory@- too many lines in file");
4406fb5685Smark 			}
4506fb5685Smark 		}
4606fb5685Smark 		nline++;
4706fb5685Smark 		a1 = truedol + 1;
4806fb5685Smark 		a2 = a1 + 1;
4906fb5685Smark 		dot++;
5006fb5685Smark 		undap2++;
5106fb5685Smark 		dol++;
5206fb5685Smark 		unddol++;
5306fb5685Smark 		truedol++;
5406fb5685Smark 		for (rdot = dot; a1 > rdot;)
5506fb5685Smark 			*--a2 = *--a1;
5606fb5685Smark 		*rdot = 0;
5706fb5685Smark 		putmark(rdot);
5806fb5685Smark 		if (f == gettty) {
5906fb5685Smark 			dirtcnt++;
6006fb5685Smark 			TSYNC();
6106fb5685Smark 		}
6206fb5685Smark 	}
6306fb5685Smark 	return (nline);
6406fb5685Smark }
6506fb5685Smark 
6606fb5685Smark appendnone()
6706fb5685Smark {
6806fb5685Smark 
69aecfa433Smark 	if(FIXUNDO) {
7006fb5685Smark 		undkind = UNDCHANGE;
7106fb5685Smark 		undap1 = undap2 = addr1;
7206fb5685Smark 	}
7306fb5685Smark }
7406fb5685Smark 
7506fb5685Smark /*
7606fb5685Smark  * Print out the argument list, with []'s around the current name.
7706fb5685Smark  */
7806fb5685Smark pargs()
7906fb5685Smark {
8006fb5685Smark 	register char **av = argv0, *as = args0;
8106fb5685Smark 	register int ac;
8206fb5685Smark 
8306fb5685Smark 	for (ac = 0; ac < argc0; ac++) {
8406fb5685Smark 		if (ac != 0)
8506fb5685Smark 			putchar(' ');
8606fb5685Smark 		if (ac + argc == argc0 - 1)
8706fb5685Smark 			printf("[");
8806fb5685Smark 		lprintf("%s", as);
8906fb5685Smark 		if (ac + argc == argc0 - 1)
9006fb5685Smark 			printf("]");
9106fb5685Smark 		as = av ? *++av : strend(as) + 1;
9206fb5685Smark 	}
9306fb5685Smark 	noonl();
9406fb5685Smark }
9506fb5685Smark 
9606fb5685Smark /*
9706fb5685Smark  * Delete lines; two cases are if we are really deleting,
9806fb5685Smark  * more commonly we are just moving lines to the undo save area.
9906fb5685Smark  */
10006fb5685Smark delete(hush)
10106fb5685Smark 	bool hush;
10206fb5685Smark {
10306fb5685Smark 	register line *a1, *a2;
10406fb5685Smark 
10506fb5685Smark 	nonzero();
106aecfa433Smark 	if(FIXUNDO) {
10706fb5685Smark 		register int (*dsavint)();
10806fb5685Smark 
10906fb5685Smark 		change();
11006fb5685Smark 		dsavint = signal(SIGINT, SIG_IGN);
11106fb5685Smark 		undkind = UNDCHANGE;
11206fb5685Smark 		a1 = addr1;
11306fb5685Smark 		squish();
11406fb5685Smark 		a2 = addr2;
11506fb5685Smark 		if (a2++ != dol) {
11606fb5685Smark 			reverse(a1, a2);
11706fb5685Smark 			reverse(a2, dol + 1);
11806fb5685Smark 			reverse(a1, dol + 1);
11906fb5685Smark 		}
12006fb5685Smark 		dol -= a2 - a1;
12106fb5685Smark 		unddel = a1 - 1;
12206fb5685Smark 		if (a1 > dol)
12306fb5685Smark 			a1 = dol;
12406fb5685Smark 		dot = a1;
12506fb5685Smark 		pkill[0] = pkill[1] = 0;
12606fb5685Smark 		signal(SIGINT, dsavint);
12706fb5685Smark 	} else {
12806fb5685Smark 		register line *a3;
12906fb5685Smark 		register int i;
13006fb5685Smark 
13106fb5685Smark 		change();
13206fb5685Smark 		a1 = addr1;
13306fb5685Smark 		a2 = addr2 + 1;
13406fb5685Smark 		a3 = truedol;
13506fb5685Smark 		i = a2 - a1;
13606fb5685Smark 		unddol -= i;
13706fb5685Smark 		undap2 -= i;
13806fb5685Smark 		dol -= i;
13906fb5685Smark 		truedol -= i;
14006fb5685Smark 		do
14106fb5685Smark 			*a1++ = *a2++;
14206fb5685Smark 		while (a2 <= a3);
14306fb5685Smark 		a1 = addr1;
14406fb5685Smark 		if (a1 > dol)
14506fb5685Smark 			a1 = dol;
14606fb5685Smark 		dot = a1;
14706fb5685Smark 	}
14806fb5685Smark 	if (!hush)
14906fb5685Smark 		killed();
15006fb5685Smark }
15106fb5685Smark 
15206fb5685Smark deletenone()
15306fb5685Smark {
15406fb5685Smark 
155aecfa433Smark 	if(FIXUNDO) {
15606fb5685Smark 		undkind = UNDCHANGE;
15706fb5685Smark 		squish();
15806fb5685Smark 		unddel = addr1;
15906fb5685Smark 	}
16006fb5685Smark }
16106fb5685Smark 
16206fb5685Smark /*
16306fb5685Smark  * Crush out the undo save area, moving the open/visual
16406fb5685Smark  * save area down in its place.
16506fb5685Smark  */
16606fb5685Smark squish()
16706fb5685Smark {
16806fb5685Smark 	register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
16906fb5685Smark 
170aecfa433Smark 	if(FIXUNDO) {
17106fb5685Smark 		if (inopen == -1)
17206fb5685Smark 			return;
17306fb5685Smark 		if (a1 < a2 && a2 < a3)
17406fb5685Smark 			do
17506fb5685Smark 				*a1++ = *a2++;
17606fb5685Smark 			while (a2 < a3);
17706fb5685Smark 		truedol -= unddol - dol;
17806fb5685Smark 		unddol = dol;
17906fb5685Smark 	}
180aecfa433Smark }
18106fb5685Smark 
18206fb5685Smark /*
18306fb5685Smark  * Join lines.  Special hacks put in spaces, two spaces if
18406fb5685Smark  * preceding line ends with '.', or no spaces if next line starts with ).
18506fb5685Smark  */
18606fb5685Smark static	int jcount, jnoop();
18706fb5685Smark 
18806fb5685Smark join(c)
18906fb5685Smark 	int c;
19006fb5685Smark {
19106fb5685Smark 	register line *a1;
19206fb5685Smark 	register char *cp, *cp1;
19306fb5685Smark 
19406fb5685Smark 	cp = genbuf;
19506fb5685Smark 	*cp = 0;
19606fb5685Smark 	for (a1 = addr1; a1 <= addr2; a1++) {
19706fb5685Smark 		getline(*a1);
19806fb5685Smark 		cp1 = linebuf;
19906fb5685Smark 		if (a1 != addr1 && c == 0) {
20006fb5685Smark 			while (*cp1 == ' ' || *cp1 == '\t')
20106fb5685Smark 				cp1++;
20206fb5685Smark 			if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
20306fb5685Smark 				if (*cp1 != ')') {
20406fb5685Smark 					*cp++ = ' ';
20506fb5685Smark 					if (cp[-2] == '.')
20606fb5685Smark 						*cp++ = ' ';
20706fb5685Smark 				}
20806fb5685Smark 			}
20906fb5685Smark 		}
21006fb5685Smark 		while (*cp++ = *cp1++)
21106fb5685Smark 			if (cp > &genbuf[LBSIZE-2])
21206fb5685Smark 				error("Line overflow|Result line of join would be too long");
21306fb5685Smark 		cp--;
21406fb5685Smark 	}
21506fb5685Smark 	strcLIN(genbuf);
21606fb5685Smark 	delete(0);
21706fb5685Smark 	jcount = 1;
218*d4cba322Smark 	if (FIXUNDO)
219*d4cba322Smark 		undap1 = undap2 = addr1;
22006fb5685Smark 	ignore(append(jnoop, --addr1));
221*d4cba322Smark 	if (FIXUNDO)
222*d4cba322Smark 		vundkind = VMANY;
22306fb5685Smark }
22406fb5685Smark 
22506fb5685Smark static
22606fb5685Smark jnoop()
22706fb5685Smark {
22806fb5685Smark 
22906fb5685Smark 	return(--jcount);
23006fb5685Smark }
23106fb5685Smark 
23206fb5685Smark /*
23306fb5685Smark  * Move and copy lines.  Hard work is done by move1 which
23406fb5685Smark  * is also called by undo.
23506fb5685Smark  */
23606fb5685Smark int	getcopy();
23706fb5685Smark 
23806fb5685Smark move()
23906fb5685Smark {
24006fb5685Smark 	register line *adt;
24106fb5685Smark 	bool iscopy = 0;
24206fb5685Smark 
24306fb5685Smark 	if (Command[0] == 'm') {
24406fb5685Smark 		setdot1();
24506fb5685Smark 		markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
24606fb5685Smark 	} else {
24706fb5685Smark 		iscopy++;
24806fb5685Smark 		setdot();
24906fb5685Smark 	}
25006fb5685Smark 	nonzero();
25106fb5685Smark 	adt = address(0);
25206fb5685Smark 	if (adt == 0)
25306fb5685Smark 		serror("%s where?|%s requires a trailing address", Command);
25406fb5685Smark 	newline();
25506fb5685Smark 	move1(iscopy, adt);
25606fb5685Smark 	killed();
25706fb5685Smark }
25806fb5685Smark 
25906fb5685Smark move1(cflag, addrt)
26006fb5685Smark 	int cflag;
26106fb5685Smark 	line *addrt;
26206fb5685Smark {
26306fb5685Smark 	register line *adt, *ad1, *ad2;
26406fb5685Smark 	int lines;
26506fb5685Smark 
26606fb5685Smark 	adt = addrt;
26706fb5685Smark 	lines = (addr2 - addr1) + 1;
26806fb5685Smark 	if (cflag) {
26906fb5685Smark 		tad1 = addr1;
27006fb5685Smark 		ad1 = dol;
27106fb5685Smark 		ignore(append(getcopy, ad1++));
27206fb5685Smark 		ad2 = dol;
27306fb5685Smark 	} else {
27406fb5685Smark 		ad2 = addr2;
27506fb5685Smark 		for (ad1 = addr1; ad1 <= ad2;)
27606fb5685Smark 			*ad1++ &= ~01;
27706fb5685Smark 		ad1 = addr1;
27806fb5685Smark 	}
27906fb5685Smark 	ad2++;
28006fb5685Smark 	if (adt < ad1) {
28106fb5685Smark 		if (adt + 1 == ad1 && !cflag && !inglobal)
28206fb5685Smark 			error("That move would do nothing!");
28306fb5685Smark 		dot = adt + (ad2 - ad1);
28406fb5685Smark 		if (++adt != ad1) {
28506fb5685Smark 			reverse(adt, ad1);
28606fb5685Smark 			reverse(ad1, ad2);
28706fb5685Smark 			reverse(adt, ad2);
28806fb5685Smark 		}
28906fb5685Smark 	} else if (adt >= ad2) {
29006fb5685Smark 		dot = adt++;
29106fb5685Smark 		reverse(ad1, ad2);
29206fb5685Smark 		reverse(ad2, adt);
29306fb5685Smark 		reverse(ad1, adt);
29406fb5685Smark 	} else
29506fb5685Smark 		error("Move to a moved line");
29606fb5685Smark 	change();
29706fb5685Smark 	if (!inglobal)
298aecfa433Smark 		if(FIXUNDO) {
29906fb5685Smark 			if (cflag) {
30006fb5685Smark 				undap1 = addrt + 1;
30106fb5685Smark 				undap2 = undap1 + lines;
30206fb5685Smark 				deletenone();
30306fb5685Smark 			} else {
30406fb5685Smark 				undkind = UNDMOVE;
30506fb5685Smark 				undap1 = addr1;
30606fb5685Smark 				undap2 = addr2;
30706fb5685Smark 				unddel = addrt;
30806fb5685Smark 				squish();
30906fb5685Smark 			}
31006fb5685Smark 		}
311aecfa433Smark }
31206fb5685Smark 
31306fb5685Smark getcopy()
31406fb5685Smark {
31506fb5685Smark 
31606fb5685Smark 	if (tad1 > addr2)
31706fb5685Smark 		return (EOF);
31806fb5685Smark 	getline(*tad1++);
31906fb5685Smark 	return (0);
32006fb5685Smark }
32106fb5685Smark 
32206fb5685Smark /*
32306fb5685Smark  * Put lines in the buffer from the undo save area.
32406fb5685Smark  */
32506fb5685Smark getput()
32606fb5685Smark {
32706fb5685Smark 
32806fb5685Smark 	if (tad1 > unddol)
32906fb5685Smark 		return (EOF);
33006fb5685Smark 	getline(*tad1++);
33106fb5685Smark 	tad1++;
33206fb5685Smark 	return (0);
33306fb5685Smark }
33406fb5685Smark 
33506fb5685Smark put()
33606fb5685Smark {
33706fb5685Smark 	register int cnt;
33806fb5685Smark 
339aecfa433Smark 	if (!FIXUNDO)
340aecfa433Smark 		error("Cannot put inside global/macro");
34106fb5685Smark 	cnt = unddol - dol;
34206fb5685Smark 	if (cnt && inopen && pkill[0] && pkill[1]) {
34306fb5685Smark 		pragged(1);
34406fb5685Smark 		return;
34506fb5685Smark 	}
34606fb5685Smark 	tad1 = dol + 1;
34706fb5685Smark 	ignore(append(getput, addr2));
34806fb5685Smark 	undkind = UNDPUT;
34906fb5685Smark 	notecnt = cnt;
35006fb5685Smark 	netchange(cnt);
35106fb5685Smark }
35206fb5685Smark 
35306fb5685Smark /*
35406fb5685Smark  * A tricky put, of a group of lines in the middle
35506fb5685Smark  * of an existing line.  Only from open/visual.
35606fb5685Smark  * Argument says pkills have meaning, e.g. called from
35706fb5685Smark  * put; it is 0 on calls from putreg.
35806fb5685Smark  */
35906fb5685Smark pragged(kill)
36006fb5685Smark 	bool kill;
36106fb5685Smark {
36206fb5685Smark 	extern char *cursor;
36306fb5685Smark 	register char *gp = &genbuf[cursor - linebuf];
36406fb5685Smark 
36506fb5685Smark 	/*
36606fb5685Smark 	 * This kind of stuff is TECO's forte.
36706fb5685Smark 	 * We just grunge along, since it cuts
36806fb5685Smark 	 * across our line-oriented model of the world
36906fb5685Smark 	 * almost scrambling our addled brain.
37006fb5685Smark 	 */
37106fb5685Smark 	if (!kill)
37206fb5685Smark 		getDOT();
37306fb5685Smark 	strcpy(genbuf, linebuf);
37406fb5685Smark 	getline(*unddol);
37506fb5685Smark 	if (kill)
37606fb5685Smark 		*pkill[1] = 0;
37706fb5685Smark 	strcat(linebuf, gp);
37806fb5685Smark 	putmark(unddol);
37906fb5685Smark 	getline(dol[1]);
38006fb5685Smark 	if (kill)
38106fb5685Smark 		strcLIN(pkill[0]);
38206fb5685Smark 	strcpy(gp, linebuf);
38306fb5685Smark 	strcLIN(genbuf);
38406fb5685Smark 	putmark(dol+1);
38506fb5685Smark 	undkind = UNDCHANGE;
38606fb5685Smark 	undap1 = dot;
38706fb5685Smark 	undap2 = dot + 1;
38806fb5685Smark 	unddel = dot - 1;
38906fb5685Smark 	undo(1);
39006fb5685Smark }
39106fb5685Smark 
39206fb5685Smark /*
39306fb5685Smark  * Shift lines, based on c.
39406fb5685Smark  * If c is neither < nor >, then this is a lisp aligning =.
39506fb5685Smark  */
39606fb5685Smark shift(c, cnt)
39706fb5685Smark 	int c;
39806fb5685Smark 	int cnt;
39906fb5685Smark {
40006fb5685Smark 	register line *addr;
40106fb5685Smark 	register char *cp;
40206fb5685Smark 	char *dp;
40306fb5685Smark 	register int i;
40406fb5685Smark 
405aecfa433Smark 	if(FIXUNDO)
40606fb5685Smark 		save12(), undkind = UNDCHANGE;
40706fb5685Smark 	cnt *= value(SHIFTWIDTH);
40806fb5685Smark 	for (addr = addr1; addr <= addr2; addr++) {
40906fb5685Smark 		dot = addr;
41006fb5685Smark #ifdef LISPCODE
41106fb5685Smark 		if (c == '=' && addr == addr1 && addr != addr2)
41206fb5685Smark 			continue;
41306fb5685Smark #endif
41406fb5685Smark 		getDOT();
41506fb5685Smark 		i = whitecnt(linebuf);
41606fb5685Smark 		switch (c) {
41706fb5685Smark 
41806fb5685Smark 		case '>':
41906fb5685Smark 			if (linebuf[0] == 0)
42006fb5685Smark 				continue;
42106fb5685Smark 			cp = genindent(i + cnt);
42206fb5685Smark 			break;
42306fb5685Smark 
42406fb5685Smark 		case '<':
42506fb5685Smark 			if (i == 0)
42606fb5685Smark 				continue;
42706fb5685Smark 			i -= cnt;
42806fb5685Smark 			cp = i > 0 ? genindent(i) : genbuf;
42906fb5685Smark 			break;
43006fb5685Smark 
43106fb5685Smark #ifdef LISPCODE
43206fb5685Smark 		default:
43306fb5685Smark 			i = lindent(addr);
43406fb5685Smark 			getDOT();
43506fb5685Smark 			cp = genindent(i);
43606fb5685Smark 			break;
43706fb5685Smark #endif
43806fb5685Smark 		}
43906fb5685Smark 		if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
44006fb5685Smark 			error("Line too long|Result line after shift would be too long");
44106fb5685Smark 		CP(cp, dp);
44206fb5685Smark 		strcLIN(genbuf);
44306fb5685Smark 		putmark(addr);
44406fb5685Smark 	}
44506fb5685Smark 	killed();
44606fb5685Smark }
44706fb5685Smark 
44806fb5685Smark /*
44906fb5685Smark  * Find a tag in the tags file.
45006fb5685Smark  * Most work here is in parsing the tags file itself.
45106fb5685Smark  */
45206fb5685Smark tagfind(quick)
45306fb5685Smark 	bool quick;
45406fb5685Smark {
45506fb5685Smark 	char cmdbuf[BUFSIZ];
45606fb5685Smark 	char filebuf[FNSIZE];
457aecfa433Smark 	char tagfbuf[128];
45806fb5685Smark 	register int c, d;
45906fb5685Smark 	bool samef = 1;
460aecfa433Smark 	int tfcount = 0;
461aecfa433Smark 	int omagic;
462aecfa433Smark 	char *fn, *fne;
463aecfa433Smark #ifdef VMUNIX
464aecfa433Smark 	/*
465aecfa433Smark 	 * We have lots of room so we bring in stdio and do
466aecfa433Smark 	 * a binary search on the tags file.
467aecfa433Smark 	 */
468aecfa433Smark # undef EOF
469aecfa433Smark # include <stdio.h>
470aecfa433Smark # undef getchar
471aecfa433Smark # undef putchar
472aecfa433Smark 	FILE *iof;
473aecfa433Smark 	char iofbuf[BUFSIZ];
474aecfa433Smark 	long mid;	/* assumed byte offset */
475aecfa433Smark 	long top, bot;	/* length of tag file */
476aecfa433Smark 	struct stat sbuf;
477aecfa433Smark #endif
47806fb5685Smark 
47906fb5685Smark 	omagic = value(MAGIC);
48006fb5685Smark 	if (!skipend()) {
48106fb5685Smark 		register char *lp = lasttag;
48206fb5685Smark 
48306fb5685Smark 		while (!iswhite(peekchar()) && !endcmd(peekchar()))
48406fb5685Smark 			if (lp < &lasttag[sizeof lasttag - 2])
48506fb5685Smark 				*lp++ = getchar();
48606fb5685Smark 			else
48706fb5685Smark 				ignchar();
48806fb5685Smark 		*lp++ = 0;
48906fb5685Smark 		if (!endcmd(peekchar()))
49006fb5685Smark badtag:
49106fb5685Smark 			error("Bad tag|Give one tag per line");
49206fb5685Smark 	} else if (lasttag[0] == 0)
49306fb5685Smark 		error("No previous tag");
49406fb5685Smark 	c = getchar();
49506fb5685Smark 	if (!endcmd(c))
49606fb5685Smark 		goto badtag;
49706fb5685Smark 	if (c == EOF)
49806fb5685Smark 		ungetchar(c);
49906fb5685Smark 	clrstats();
500aecfa433Smark 
501aecfa433Smark 	/*
502aecfa433Smark 	 * Loop once for each file in tags "path".
503aecfa433Smark 	 */
504aecfa433Smark 	CP(tagfbuf, svalue(TAGS));
505aecfa433Smark 	fne = tagfbuf - 1;
506aecfa433Smark 	while (fne) {
507aecfa433Smark 		fn = ++fne;
508aecfa433Smark 		while (*fne && *fne != ' ')
509aecfa433Smark 			fne++;
510aecfa433Smark 		if (*fne == 0)
511aecfa433Smark 			fne = 0;	/* done, quit after this time */
512aecfa433Smark 		else
513aecfa433Smark 			*fne = 0;	/* null terminate filename */
514aecfa433Smark #ifdef VMUNIX
515aecfa433Smark 		iof = fopen(fn, "r");
516aecfa433Smark 		if (iof == NULL)
517aecfa433Smark 			continue;
518aecfa433Smark 		tfcount++;
519aecfa433Smark 		setbuf(iof, iofbuf);
520aecfa433Smark 		fstat(fileno(iof), &sbuf);
521aecfa433Smark 		top = sbuf.st_size;
522aecfa433Smark 		if (top == 0L || iof == NULL)
523aecfa433Smark 			top = -1L;
524aecfa433Smark 		bot = 0L;
525aecfa433Smark 		while (top >= bot) {
526aecfa433Smark #else
527aecfa433Smark 		/*
528aecfa433Smark 		 * Avoid stdio and scan tag file linearly.
529aecfa433Smark 		 */
530aecfa433Smark 		io = open(fn, 0);
531aecfa433Smark 		if (io<0)
532aecfa433Smark 			continue;
533*d4cba322Smark 		tfcount++;
53406fb5685Smark 		while (getfile() == 0) {
535aecfa433Smark #endif
536aecfa433Smark 			/* loop for each tags file entry */
53706fb5685Smark 			register char *cp = linebuf;
53806fb5685Smark 			register char *lp = lasttag;
53906fb5685Smark 			char *oglobp;
54006fb5685Smark 
541aecfa433Smark #ifdef VMUNIX
542aecfa433Smark 			mid = (top + bot) / 2;
543aecfa433Smark 			fseek(iof, mid, 0);
544aecfa433Smark 			if (mid > 0)	/* to get first tag in file to work */
545aecfa433Smark 				fgets(linebuf, sizeof linebuf, iof);	/* scan to next \n */
546aecfa433Smark 			fgets(linebuf, sizeof linebuf, iof);	/* get a line */
547aecfa433Smark 			linebuf[strlen(linebuf)-1] = 0;	/* was '\n' */
548aecfa433Smark #endif
54906fb5685Smark 			while (*cp && *lp == *cp)
55006fb5685Smark 				cp++, lp++;
551*d4cba322Smark 			if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 || lp-lasttag < value(TAGLENGTH))) {
552aecfa433Smark #ifdef VMUNIX
553aecfa433Smark 				if (*lp > *cp)
554aecfa433Smark 					bot = mid + 1;
555aecfa433Smark 				else
556aecfa433Smark 					top = mid - 1;
557aecfa433Smark #endif
558aecfa433Smark 				/* Not this tag.  Try the next */
55906fb5685Smark 				continue;
560aecfa433Smark 			}
561aecfa433Smark 
562aecfa433Smark 			/*
563aecfa433Smark 			 * We found the tag.  Decode the line in the file.
564aecfa433Smark 			 */
565aecfa433Smark #ifdef VMUNIX
566aecfa433Smark 			fclose(iof);
567aecfa433Smark #else
56806fb5685Smark 			close(io);
569aecfa433Smark #endif
570*d4cba322Smark 			/* Rest of tag if abbreviated */
571*d4cba322Smark 			while (!iswhite(*cp))
572*d4cba322Smark 				cp++;
573*d4cba322Smark 
574aecfa433Smark 			/* name of file */
57506fb5685Smark 			while (*cp && iswhite(*cp))
57606fb5685Smark 				cp++;
57706fb5685Smark 			if (!*cp)
57806fb5685Smark badtags:
57906fb5685Smark 				serror("%s: Bad tags file entry", lasttag);
58006fb5685Smark 			lp = filebuf;
58106fb5685Smark 			while (*cp && *cp != ' ' && *cp != '\t') {
58206fb5685Smark 				if (lp < &filebuf[sizeof filebuf - 2])
58306fb5685Smark 					*lp++ = *cp;
58406fb5685Smark 				cp++;
58506fb5685Smark 			}
58606fb5685Smark 			*lp++ = 0;
587aecfa433Smark 
58806fb5685Smark 			if (*cp == 0)
58906fb5685Smark 				goto badtags;
59006fb5685Smark 			if (dol != zero) {
59106fb5685Smark 				/*
59206fb5685Smark 				 * Save current position in 't for ^^ in visual.
59306fb5685Smark 				 */
59406fb5685Smark 				names['t'-'a'] = *dot &~ 01;
59506fb5685Smark 				if (inopen) {
596*d4cba322Smark 					extern char *ncols['z'-'a'+2];
59706fb5685Smark 					extern char *cursor;
59806fb5685Smark 
59906fb5685Smark 					ncols['t'-'a'] = cursor;
60006fb5685Smark 				}
60106fb5685Smark 			}
60206fb5685Smark 			strcpy(cmdbuf, cp);
60306fb5685Smark 			if (strcmp(filebuf, savedfile) || !edited) {
60406fb5685Smark 				char cmdbuf2[sizeof filebuf + 10];
60506fb5685Smark 
606aecfa433Smark 				/* Different file.  Do autowrite & get it. */
60706fb5685Smark 				if (!quick) {
60806fb5685Smark 					ckaw();
60906fb5685Smark 					if (chng && dol > zero)
61006fb5685Smark 						error("No write@since last change (:tag! overrides)");
61106fb5685Smark 				}
61206fb5685Smark 				oglobp = globp;
61306fb5685Smark 				strcpy(cmdbuf2, "e! ");
61406fb5685Smark 				strcat(cmdbuf2, filebuf);
61506fb5685Smark 				globp = cmdbuf2;
61606fb5685Smark 				d = peekc; ungetchar(0);
617aecfa433Smark 				commands(1, 1);
618aecfa433Smark 				peekc = d;
619aecfa433Smark 				globp = oglobp;
620aecfa433Smark 				value(MAGIC) = omagic;
621aecfa433Smark 				samef = 0;
622aecfa433Smark 			}
623aecfa433Smark 
624aecfa433Smark 			/*
625aecfa433Smark 			 * Look for pattern in the current file.
626aecfa433Smark 			 */
627aecfa433Smark 			oglobp = globp;
628aecfa433Smark 			globp = cmdbuf;
629aecfa433Smark 			d = peekc; ungetchar(0);
630aecfa433Smark 			if (samef)
631aecfa433Smark 				markpr(dot);
632f80bacf1Smark 			/*
633f80bacf1Smark 			 * BUG: if it isn't found (user edited header
634f80bacf1Smark 			 * line) we get left in nomagic mode.
635f80bacf1Smark 			 */
636f80bacf1Smark 			value(MAGIC) = 0;
63706fb5685Smark 			commands(1, 1);
63806fb5685Smark 			peekc = d;
63906fb5685Smark 			globp = oglobp;
640f80bacf1Smark 			value(MAGIC) = omagic;
64106fb5685Smark 			return;
642aecfa433Smark 		}	/* end of "for each tag in file" */
643aecfa433Smark 
644aecfa433Smark 		/*
645aecfa433Smark 		 * No such tag in this file.  Close it and try the next.
646aecfa433Smark 		 */
647aecfa433Smark #ifdef VMUNIX
648aecfa433Smark 		fclose(iof);
649aecfa433Smark #else
650aecfa433Smark 		close(io);
651aecfa433Smark #endif
652aecfa433Smark 	}	/* end of "for each file in path" */
653aecfa433Smark 	if (tfcount <= 0)
65406fb5685Smark 		error("No tags file");
655aecfa433Smark 	else
65606fb5685Smark 		serror("%s: No such tag@in tags file", lasttag);
65706fb5685Smark }
65806fb5685Smark 
65906fb5685Smark /*
66006fb5685Smark  * Save lines from addr1 thru addr2 as though
66106fb5685Smark  * they had been deleted.
66206fb5685Smark  */
66306fb5685Smark yank()
66406fb5685Smark {
66506fb5685Smark 
666aecfa433Smark 	if (!FIXUNDO)
667aecfa433Smark 		error("Can't yank inside global/macro");
66806fb5685Smark 	save12();
66906fb5685Smark 	undkind = UNDNONE;
67006fb5685Smark 	killcnt(addr2 - addr1 + 1);
67106fb5685Smark }
67206fb5685Smark 
67306fb5685Smark /*
67406fb5685Smark  * z command; print windows of text in the file.
67506fb5685Smark  *
67606fb5685Smark  * If this seems unreasonably arcane, the reasons
67706fb5685Smark  * are historical.  This is one of the first commands
67806fb5685Smark  * added to the first ex (then called en) and the
67906fb5685Smark  * number of facilities here were the major advantage
68006fb5685Smark  * of en over ed since they allowed more use to be
68106fb5685Smark  * made of fast terminals w/o typing .,.22p all the time.
68206fb5685Smark  */
68306fb5685Smark bool	zhadpr;
68406fb5685Smark bool	znoclear;
68506fb5685Smark short	zweight;
68606fb5685Smark 
68706fb5685Smark zop(hadpr)
68806fb5685Smark 	int hadpr;
68906fb5685Smark {
69006fb5685Smark 	register int c, lines, op;
69106fb5685Smark 	bool excl;
69206fb5685Smark 
69306fb5685Smark 	zhadpr = hadpr;
69406fb5685Smark 	notempty();
69506fb5685Smark 	znoclear = 0;
69606fb5685Smark 	zweight = 0;
69706fb5685Smark 	excl = exclam();
69806fb5685Smark 	switch (c = op = getchar()) {
69906fb5685Smark 
70006fb5685Smark 	case '^':
70106fb5685Smark 		zweight = 1;
70206fb5685Smark 	case '-':
70306fb5685Smark 	case '+':
70406fb5685Smark 		while (peekchar() == op) {
70506fb5685Smark 			ignchar();
70606fb5685Smark 			zweight++;
70706fb5685Smark 		}
70806fb5685Smark 	case '=':
70906fb5685Smark 	case '.':
71006fb5685Smark 		c = getchar();
71106fb5685Smark 		break;
71206fb5685Smark 
71306fb5685Smark 	case EOF:
71406fb5685Smark 		znoclear++;
71506fb5685Smark 		break;
71606fb5685Smark 
71706fb5685Smark 	default:
71806fb5685Smark 		op = 0;
71906fb5685Smark 		break;
72006fb5685Smark 	}
72106fb5685Smark 	if (isdigit(c)) {
72206fb5685Smark 		lines = c - '0';
72306fb5685Smark 		for(;;) {
72406fb5685Smark 			c = getchar();
72506fb5685Smark 			if (!isdigit(c))
72606fb5685Smark 				break;
72706fb5685Smark 			lines *= 10;
72806fb5685Smark 			lines += c - '0';
72906fb5685Smark 		}
73006fb5685Smark 		if (lines < LINES)
73106fb5685Smark 			znoclear++;
73206fb5685Smark 		value(WINDOW) = lines;
73306fb5685Smark 		if (op == '=')
73406fb5685Smark 			lines += 2;
73506fb5685Smark 	} else
73606fb5685Smark 		lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL);
73706fb5685Smark 	if (inopen || c != EOF) {
73806fb5685Smark 		ungetchar(c);
73906fb5685Smark 		newline();
74006fb5685Smark 	}
74106fb5685Smark 	addr1 = addr2;
74206fb5685Smark 	if (addr2 == 0 && dot < dol && op == 0)
74306fb5685Smark 		addr1 = addr2 = dot+1;
74406fb5685Smark 	setdot();
74506fb5685Smark 	zop2(lines, op);
74606fb5685Smark }
74706fb5685Smark 
74806fb5685Smark zop2(lines, op)
74906fb5685Smark 	register int lines;
75006fb5685Smark 	register int op;
75106fb5685Smark {
75206fb5685Smark 	register line *split;
75306fb5685Smark 
75406fb5685Smark 	split = NULL;
75506fb5685Smark 	switch (op) {
75606fb5685Smark 
75706fb5685Smark 	case EOF:
75806fb5685Smark 		if (addr2 == dol)
75906fb5685Smark 			error("\nAt EOF");
76006fb5685Smark 	case '+':
76106fb5685Smark 		if (addr2 == dol)
76206fb5685Smark 			error("At EOF");
76306fb5685Smark 		addr2 += lines * zweight;
76406fb5685Smark 		if (addr2 > dol)
76506fb5685Smark 			error("Hit BOTTOM");
76606fb5685Smark 		addr2++;
76706fb5685Smark 	default:
76806fb5685Smark 		addr1 = addr2;
76906fb5685Smark 		addr2 += lines-1;
77006fb5685Smark 		dot = addr2;
77106fb5685Smark 		break;
77206fb5685Smark 
77306fb5685Smark 	case '=':
77406fb5685Smark 	case '.':
77506fb5685Smark 		znoclear = 0;
77606fb5685Smark 		lines--;
77706fb5685Smark 		lines >>= 1;
77806fb5685Smark 		if (op == '=')
77906fb5685Smark 			lines--;
78006fb5685Smark 		addr1 = addr2 - lines;
78106fb5685Smark 		if (op == '=')
78206fb5685Smark 			dot = split = addr2;
78306fb5685Smark 		addr2 += lines;
78406fb5685Smark 		if (op == '.') {
78506fb5685Smark 			markDOT();
78606fb5685Smark 			dot = addr2;
78706fb5685Smark 		}
78806fb5685Smark 		break;
78906fb5685Smark 
79006fb5685Smark 	case '^':
79106fb5685Smark 	case '-':
79206fb5685Smark 		addr2 -= lines * zweight;
79306fb5685Smark 		if (addr2 < one)
79406fb5685Smark 			error("Hit TOP");
79506fb5685Smark 		lines--;
79606fb5685Smark 		addr1 = addr2 - lines;
79706fb5685Smark 		dot = addr2;
79806fb5685Smark 		break;
79906fb5685Smark 	}
80006fb5685Smark 	if (addr1 <= zero)
80106fb5685Smark 		addr1 = one;
80206fb5685Smark 	if (addr2 > dol)
80306fb5685Smark 		addr2 = dol;
80406fb5685Smark 	if (dot > dol)
80506fb5685Smark 		dot = dol;
80606fb5685Smark 	if (addr1 > addr2)
80706fb5685Smark 		return;
80806fb5685Smark 	if (op == EOF && zhadpr) {
80906fb5685Smark 		getline(*addr1);
81006fb5685Smark 		putchar('\r' | QUOTE);
81106fb5685Smark 		shudclob = 1;
81206fb5685Smark 	} else if (znoclear == 0 && CL != NOSTR && !inopen) {
81306fb5685Smark 		flush1();
81406fb5685Smark 		vclear();
81506fb5685Smark 	}
81606fb5685Smark 	if (addr2 - addr1 > 1)
81706fb5685Smark 		pstart();
81806fb5685Smark 	if (split) {
81906fb5685Smark 		plines(addr1, split - 1, 0);
82006fb5685Smark 		splitit();
82106fb5685Smark 		plines(split, split, 0);
82206fb5685Smark 		splitit();
82306fb5685Smark 		addr1 = split + 1;
82406fb5685Smark 	}
82506fb5685Smark 	plines(addr1, addr2, 0);
82606fb5685Smark }
82706fb5685Smark 
82806fb5685Smark static
82906fb5685Smark splitit()
83006fb5685Smark {
83106fb5685Smark 	register int l;
83206fb5685Smark 
83306fb5685Smark 	for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
83406fb5685Smark 		putchar('-');
83506fb5685Smark 	putnl();
83606fb5685Smark }
83706fb5685Smark 
83806fb5685Smark plines(adr1, adr2, movedot)
83906fb5685Smark 	line *adr1;
84006fb5685Smark 	register line *adr2;
84106fb5685Smark 	bool movedot;
84206fb5685Smark {
84306fb5685Smark 	register line *addr;
84406fb5685Smark 
84506fb5685Smark 	pofix();
84606fb5685Smark 	for (addr = adr1; addr <= adr2; addr++) {
84706fb5685Smark 		getline(*addr);
84806fb5685Smark 		pline(lineno(addr));
84906fb5685Smark 		if (inopen)
85006fb5685Smark 			putchar('\n' | QUOTE);
85106fb5685Smark 		if (movedot)
85206fb5685Smark 			dot = addr;
85306fb5685Smark 	}
85406fb5685Smark }
85506fb5685Smark 
85606fb5685Smark pofix()
85706fb5685Smark {
85806fb5685Smark 
85906fb5685Smark 	if (inopen && Outchar != termchar) {
86006fb5685Smark 		vnfl();
86106fb5685Smark 		setoutt();
86206fb5685Smark 	}
86306fb5685Smark }
86406fb5685Smark 
86506fb5685Smark /*
86606fb5685Smark  * Dudley doright to the rescue.
86706fb5685Smark  * Undo saves the day again.
86806fb5685Smark  * A tip of the hatlo hat to Warren Teitleman
86906fb5685Smark  * who made undo as useful as do.
87006fb5685Smark  *
87106fb5685Smark  * Command level undo works easily because
87206fb5685Smark  * the editor has a unique temporary file
87306fb5685Smark  * index for every line which ever existed.
87406fb5685Smark  * We don't have to save large blocks of text,
87506fb5685Smark  * only the indices which are small.  We do this
87606fb5685Smark  * by moving them to after the last line in the
87706fb5685Smark  * line buffer array, and marking down info
87806fb5685Smark  * about whence they came.
87906fb5685Smark  *
88006fb5685Smark  * Undo is its own inverse.
88106fb5685Smark  */
88206fb5685Smark undo(c)
88306fb5685Smark 	bool c;
88406fb5685Smark {
88506fb5685Smark 	register int i;
88606fb5685Smark 	register line *jp, *kp;
88706fb5685Smark 	line *dolp1, *newdol, *newadot;
88806fb5685Smark 
889aecfa433Smark #ifdef TRACE
890aecfa433Smark 	if (trace)
891aecfa433Smark 		vudump("before undo");
892aecfa433Smark #endif
89306fb5685Smark 	if (inglobal && inopen <= 0)
89406fb5685Smark 		error("Can't undo in global@commands");
89506fb5685Smark 	if (!c)
89606fb5685Smark 		somechange();
89706fb5685Smark 	pkill[0] = pkill[1] = 0;
89806fb5685Smark 	change();
89906fb5685Smark 	if (undkind == UNDMOVE) {
90006fb5685Smark  		/*
90106fb5685Smark 		 * Command to be undone is a move command.
90206fb5685Smark 		 * This is handled as a special case by noting that
90306fb5685Smark 		 * a move "a,b m c" can be inverted by another move.
90406fb5685Smark 		 */
90506fb5685Smark 		if ((i = (jp = unddel) - undap2) > 0) {
90606fb5685Smark 			/*
90706fb5685Smark 			 * when c > b inverse is a+(c-b),c m a-1
90806fb5685Smark 			 */
90906fb5685Smark 			addr2 = jp;
91006fb5685Smark 			addr1 = (jp = undap1) + i;
91106fb5685Smark 			unddel = jp-1;
91206fb5685Smark 		} else {
91306fb5685Smark 			/*
91406fb5685Smark 			 * when b > c inverse is  c+1,c+1+(b-a) m b
91506fb5685Smark 			 */
91606fb5685Smark 			addr1 = ++jp;
91706fb5685Smark 			addr2 = jp + ((unddel = undap2) - undap1);
91806fb5685Smark 		}
91906fb5685Smark 		kp = undap1;
92006fb5685Smark 		move1(0, unddel);
92106fb5685Smark 		dot = kp;
92206fb5685Smark 		Command = "move";
92306fb5685Smark 		killed();
92406fb5685Smark 	} else {
92506fb5685Smark 		int cnt;
92606fb5685Smark 
92706fb5685Smark 		newadot = dot;
92806fb5685Smark 		cnt = lineDOL();
92906fb5685Smark 		newdol = dol;
93006fb5685Smark 		dolp1 = dol + 1;
93106fb5685Smark 		/*
93206fb5685Smark 		 * Command to be undone is a non-move.
93306fb5685Smark 		 * All such commands are treated as a combination of
93406fb5685Smark 		 * a delete command and a append command.
93506fb5685Smark 		 * We first move the lines appended by the last command
93606fb5685Smark 		 * from undap1 to undap2-1 so that they are just before the
93706fb5685Smark 		 * saved deleted lines.
93806fb5685Smark 		 */
93906fb5685Smark 		if ((i = (kp = undap2) - (jp = undap1)) > 0) {
94006fb5685Smark 			if (kp != dolp1) {
94106fb5685Smark 				reverse(jp, kp);
94206fb5685Smark 				reverse(kp, dolp1);
94306fb5685Smark 				reverse(jp, dolp1);
94406fb5685Smark 			}
94506fb5685Smark 			/*
94606fb5685Smark 			 * Account for possible backward motion of target
94706fb5685Smark 			 * for restoration of saved deleted lines.
94806fb5685Smark 			 */
94906fb5685Smark 			if (unddel >= jp)
95006fb5685Smark 				unddel -= i;
95106fb5685Smark 			newdol -= i;
95206fb5685Smark 			/*
95306fb5685Smark 			 * For the case where no lines are restored, dot
95406fb5685Smark 			 * is the line before the first line deleted.
95506fb5685Smark 			 */
95606fb5685Smark 			dot = jp-1;
95706fb5685Smark 		}
95806fb5685Smark 		/*
95906fb5685Smark 		 * Now put the deleted lines, if any, back where they were.
96006fb5685Smark 		 * Basic operation is: dol+1,unddol m unddel
96106fb5685Smark 		 */
96206fb5685Smark 		if (undkind == UNDPUT) {
96306fb5685Smark 			unddel = undap1 - 1;
96406fb5685Smark 			squish();
96506fb5685Smark 		}
96606fb5685Smark 		jp = unddel + 1;
96706fb5685Smark 		if ((i = (kp = unddol) - dol) > 0) {
96806fb5685Smark 			if (jp != dolp1) {
96906fb5685Smark 				reverse(jp, dolp1);
97006fb5685Smark 				reverse(dolp1, ++kp);
97106fb5685Smark 				reverse(jp, kp);
97206fb5685Smark 			}
97306fb5685Smark 			/*
97406fb5685Smark 			 * Account for possible forward motion of the target
97506fb5685Smark 			 * for restoration of the deleted lines.
97606fb5685Smark 			 */
97706fb5685Smark 			if (undap1 >= jp)
97806fb5685Smark 				undap1 += i;
97906fb5685Smark 			/*
98006fb5685Smark 			 * Dot is the first resurrected line.
98106fb5685Smark 			 */
98206fb5685Smark 			dot = jp;
98306fb5685Smark 			newdol += i;
98406fb5685Smark 		}
98506fb5685Smark 		/*
98606fb5685Smark 		 * Clean up so we are invertible
98706fb5685Smark 		 */
98806fb5685Smark 		unddel = undap1 - 1;
98906fb5685Smark 		undap1 = jp;
99006fb5685Smark 		undap2 = jp + i;
99106fb5685Smark 		dol = newdol;
99206fb5685Smark 		netchHAD(cnt);
99306fb5685Smark 		if (undkind == UNDALL) {
99406fb5685Smark 			dot = undadot;
99506fb5685Smark 			undadot = newadot;
996aecfa433Smark 		} else
99706fb5685Smark 			undkind = UNDCHANGE;
99806fb5685Smark 	}
999*d4cba322Smark 	/*
1000*d4cba322Smark 	 * Defensive programming - after a munged undadot.
1001*d4cba322Smark 	 * Also handle empty buffer case.
1002*d4cba322Smark 	 */
1003*d4cba322Smark 	if ((dot <= zero || dot > dol) && dot != dol)
100406fb5685Smark 		dot = one;
1005aecfa433Smark #ifdef TRACE
1006aecfa433Smark 	if (trace)
1007aecfa433Smark 		vudump("after undo");
1008aecfa433Smark #endif
100906fb5685Smark }
101006fb5685Smark 
101106fb5685Smark /*
101206fb5685Smark  * Be (almost completely) sure there really
101306fb5685Smark  * was a change, before claiming to undo.
101406fb5685Smark  */
101506fb5685Smark somechange()
101606fb5685Smark {
101706fb5685Smark 	register line *ip, *jp;
101806fb5685Smark 
101906fb5685Smark 	switch (undkind) {
102006fb5685Smark 
102106fb5685Smark 	case UNDMOVE:
102206fb5685Smark 		return;
102306fb5685Smark 
102406fb5685Smark 	case UNDCHANGE:
102506fb5685Smark 		if (undap1 == undap2 && dol == unddol)
102606fb5685Smark 			break;
102706fb5685Smark 		return;
102806fb5685Smark 
102906fb5685Smark 	case UNDPUT:
103006fb5685Smark 		if (undap1 != undap2)
103106fb5685Smark 			return;
103206fb5685Smark 		break;
103306fb5685Smark 
103406fb5685Smark 	case UNDALL:
103506fb5685Smark 		if (unddol - dol != lineDOL())
103606fb5685Smark 			return;
103706fb5685Smark 		for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
103806fb5685Smark 			if ((*ip &~ 01) != (*jp &~ 01))
103906fb5685Smark 				return;
104006fb5685Smark 		break;
104106fb5685Smark 
104206fb5685Smark 	case UNDNONE:
104306fb5685Smark 		error("Nothing to undo");
104406fb5685Smark 	}
104506fb5685Smark 	error("Nothing changed|Last undoable command didn't change anything");
104606fb5685Smark }
104706fb5685Smark 
104806fb5685Smark /*
104906fb5685Smark  * Map command:
105006fb5685Smark  * map src dest
105106fb5685Smark  */
1052*d4cba322Smark mapcmd(un, ab)
105306fb5685Smark 	int un;	/* true if this is unmap command */
1054*d4cba322Smark 	int ab;	/* true if this is abbr command */
105506fb5685Smark {
1056*d4cba322Smark 	char lhs[100], rhs[100];	/* max sizes resp. */
105706fb5685Smark 	register char *p;
105806fb5685Smark 	register char c;
105906fb5685Smark 	char *dname;
1060aecfa433Smark 	struct maps *mp;	/* the map structure we are working on */
106106fb5685Smark 
1062*d4cba322Smark 	mp = ab ? abbrevs : exclam() ? immacs : arrows;
106306fb5685Smark 	if (skipend()) {
106406fb5685Smark 		int i;
106506fb5685Smark 
106606fb5685Smark 		/* print current mapping values */
106706fb5685Smark 		if (peekchar() != EOF)
106806fb5685Smark 			ignchar();
1069*d4cba322Smark 		if (un)
1070*d4cba322Smark 			error("Missing lhs");
107106fb5685Smark 		if (inopen)
107206fb5685Smark 			pofix();
1073aecfa433Smark 		for (i=0; mp[i].mapto; i++)
1074aecfa433Smark 			if (mp[i].cap) {
1075aecfa433Smark 				lprintf("%s", mp[i].descr);
107606fb5685Smark 				putchar('\t');
1077aecfa433Smark 				lprintf("%s", mp[i].cap);
107806fb5685Smark 				putchar('\t');
1079aecfa433Smark 				lprintf("%s", mp[i].mapto);
108006fb5685Smark 				putNFL();
108106fb5685Smark 			}
108206fb5685Smark 		return;
108306fb5685Smark 	}
108406fb5685Smark 
108506fb5685Smark 	ignore(skipwh());
108606fb5685Smark 	for (p=lhs; ; ) {
108706fb5685Smark 		c = getchar();
108806fb5685Smark 		if (c == CTRL(v)) {
108906fb5685Smark 			c = getchar();
1090*d4cba322Smark 		} else if (!un && any(c, " \t")) {
1091*d4cba322Smark 			/* End of lhs */
109206fb5685Smark 			break;
1093*d4cba322Smark 		} else if (endcmd(c) && c!='"') {
109406fb5685Smark 			ungetchar(c);
109506fb5685Smark 			if (un) {
109606fb5685Smark 				newline();
1097*d4cba322Smark 				*p = 0;
1098aecfa433Smark 				addmac(lhs, NOSTR, NOSTR, mp);
109906fb5685Smark 				return;
110006fb5685Smark 			} else
110106fb5685Smark 				error("Missing rhs");
110206fb5685Smark 		}
110306fb5685Smark 		*p++ = c;
110406fb5685Smark 	}
110506fb5685Smark 	*p = 0;
110606fb5685Smark 
110706fb5685Smark 	if (skipend())
110806fb5685Smark 		error("Missing rhs");
110906fb5685Smark 	for (p=rhs; ; ) {
111006fb5685Smark 		c = getchar();
111106fb5685Smark 		if (c == CTRL(v)) {
111206fb5685Smark 			c = getchar();
1113*d4cba322Smark 		} else if (endcmd(c) && c!='"') {
111406fb5685Smark 			ungetchar(c);
111506fb5685Smark 			break;
111606fb5685Smark 		}
111706fb5685Smark 		*p++ = c;
111806fb5685Smark 	}
111906fb5685Smark 	*p = 0;
112006fb5685Smark 	newline();
112106fb5685Smark 	/*
112206fb5685Smark 	 * Special hack for function keys: #1 means key f1, etc.
112306fb5685Smark 	 * If the terminal doesn't have function keys, we just use #1.
112406fb5685Smark 	 */
112506fb5685Smark 	if (lhs[0] == '#') {
112606fb5685Smark 		char *fnkey;
112706fb5685Smark 		char *fkey();
112806fb5685Smark 		char funkey[3];
112906fb5685Smark 
113006fb5685Smark 		fnkey = fkey(lhs[1] - '0');
113106fb5685Smark 		funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
113206fb5685Smark 		if (fnkey)
113306fb5685Smark 			strcpy(lhs, fnkey);
113406fb5685Smark 		dname = funkey;
113506fb5685Smark 	} else {
113606fb5685Smark 		dname = lhs;
113706fb5685Smark 	}
1138aecfa433Smark 	addmac(lhs,rhs,dname,mp);
113906fb5685Smark }
114006fb5685Smark 
114106fb5685Smark /*
114206fb5685Smark  * Add a macro definition to those that already exist. The sequence of
114306fb5685Smark  * chars "src" is mapped into "dest". If src is already mapped into something
114406fb5685Smark  * this overrides the mapping. There is no recursion. Unmap is done by
1145aecfa433Smark  * using NOSTR for dest.  Dname is what to show in listings.  mp is
1146aecfa433Smark  * the structure to affect (arrows, etc).
114706fb5685Smark  */
1148aecfa433Smark addmac(src,dest,dname,mp)
114906fb5685Smark 	register char *src, *dest, *dname;
1150aecfa433Smark 	register struct maps *mp;
115106fb5685Smark {
115206fb5685Smark 	register int slot, zer;
115306fb5685Smark 
1154*d4cba322Smark #ifdef TRACE
1155*d4cba322Smark 	if (trace)
1156*d4cba322Smark 		fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
1157*d4cba322Smark #endif
1158aecfa433Smark 	if (dest && mp==arrows) {
115906fb5685Smark 		/* Make sure user doesn't screw himself */
116006fb5685Smark 		/*
1161f80bacf1Smark 		 * Prevent tail recursion. We really should be
1162f80bacf1Smark 		 * checking to see if src is a suffix of dest
1163aecfa433Smark 		 * but this makes mapping involving escapes that
1164aecfa433Smark 		 * is reasonable mess up.
116506fb5685Smark 		 */
1166f80bacf1Smark 		if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
1167f80bacf1Smark 			error("No tail recursion");
116806fb5685Smark 		/*
116906fb5685Smark 		 * We don't let the user rob himself of ":", and making
117006fb5685Smark 		 * multi char words is a bad idea so we don't allow it.
117106fb5685Smark 		 * Note that if user sets mapinput and maps all of return,
117206fb5685Smark 		 * linefeed, and escape, he can screw himself. This is
117306fb5685Smark 		 * so weird I don't bother to check for it.
117406fb5685Smark 		 */
117506fb5685Smark 		if (isalpha(src[0]) && src[1] || any(src[0],":"))
117606fb5685Smark 			error("Too dangerous to map that");
1177aecfa433Smark 	}
1178aecfa433Smark 	else if (dest) {
1179aecfa433Smark 		/* check for tail recursion in input mode: fussier */
1180aecfa433Smark 		if (eq(src, dest+strlen(dest)-strlen(src)))
1181aecfa433Smark 			error("No tail recursion");
1182aecfa433Smark 	}
118306fb5685Smark 	/*
118406fb5685Smark 	 * If the src were null it would cause the dest to
118506fb5685Smark 	 * be mapped always forever. This is not good.
118606fb5685Smark 	 */
1187aecfa433Smark 	if (src == NOSTR || src[0] == 0)
1188aecfa433Smark 		error("Missing lhs");
118906fb5685Smark 
119006fb5685Smark 	/* see if we already have a def for src */
119106fb5685Smark 	zer = -1;
1192aecfa433Smark 	for (slot=0; mp[slot].mapto; slot++) {
1193aecfa433Smark 		if (mp[slot].cap) {
1194*d4cba322Smark 			if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
119506fb5685Smark 				break;	/* if so, reuse slot */
119606fb5685Smark 		} else {
119706fb5685Smark 			zer = slot;	/* remember an empty slot */
119806fb5685Smark 		}
119906fb5685Smark 	}
120006fb5685Smark 
120106fb5685Smark 	if (dest == NOSTR) {
120206fb5685Smark 		/* unmap */
1203aecfa433Smark 		if (mp[slot].cap) {
1204aecfa433Smark 			mp[slot].cap = NOSTR;
1205aecfa433Smark 			mp[slot].descr = NOSTR;
120606fb5685Smark 		} else {
120706fb5685Smark 			error("Not mapped|That macro wasn't mapped");
120806fb5685Smark 		}
120906fb5685Smark 		return;
121006fb5685Smark 	}
121106fb5685Smark 
121206fb5685Smark 	/* reuse empty slot, if we found one and src isn't already defined */
1213aecfa433Smark 	if (zer >= 0 && mp[slot].mapto == 0)
121406fb5685Smark 		slot = zer;
121506fb5685Smark 
121606fb5685Smark 	/* if not, append to end */
121706fb5685Smark 	if (slot >= MAXNOMACS)
121806fb5685Smark 		error("Too many macros");
121906fb5685Smark 	if (msnext == 0)	/* first time */
122006fb5685Smark 		msnext = mapspace;
122106fb5685Smark 	/* Check is a bit conservative, we charge for dname even if reusing src */
122206fb5685Smark 	if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS)
122306fb5685Smark 		error("Too much macro text");
122406fb5685Smark 	CP(msnext, src);
1225aecfa433Smark 	mp[slot].cap = msnext;
122606fb5685Smark 	msnext += strlen(src) + 1;	/* plus 1 for null on the end */
122706fb5685Smark 	CP(msnext, dest);
1228aecfa433Smark 	mp[slot].mapto = msnext;
122906fb5685Smark 	msnext += strlen(dest) + 1;
123006fb5685Smark 	if (dname) {
123106fb5685Smark 		CP(msnext, dname);
1232aecfa433Smark 		mp[slot].descr = msnext;
123306fb5685Smark 		msnext += strlen(dname) + 1;
123406fb5685Smark 	} else {
123506fb5685Smark 		/* default descr to string user enters */
1236aecfa433Smark 		mp[slot].descr = src;
123706fb5685Smark 	}
123806fb5685Smark }
123906fb5685Smark 
124006fb5685Smark /*
124106fb5685Smark  * Implements macros from command mode. c is the buffer to
124206fb5685Smark  * get the macro from.
124306fb5685Smark  */
124406fb5685Smark cmdmac(c)
124506fb5685Smark char c;
124606fb5685Smark {
124706fb5685Smark 	char macbuf[BUFSIZ];
124806fb5685Smark 	line *ad, *a1, *a2;
124906fb5685Smark 	char *oglobp;
125006fb5685Smark 	char pk;
125106fb5685Smark 	bool oinglobal;
125206fb5685Smark 
125306fb5685Smark 	lastmac = c;
125406fb5685Smark 	oglobp = globp;
125506fb5685Smark 	oinglobal = inglobal;
125606fb5685Smark 	pk = peekc; peekc = 0;
125706fb5685Smark 	if (inglobal < 2)
125806fb5685Smark 		inglobal = 1;
125906fb5685Smark 	regbuf(c, macbuf, sizeof(macbuf));
126006fb5685Smark 	a1 = addr1; a2 = addr2;
126106fb5685Smark 	for (ad=a1; ad<=a2; ad++) {
126206fb5685Smark 		globp = macbuf;
126306fb5685Smark 		dot = ad;
126406fb5685Smark 		commands(1,1);
126506fb5685Smark 	}
126606fb5685Smark 	globp = oglobp;
126706fb5685Smark 	inglobal = oinglobal;
126806fb5685Smark 	peekc = pk;
126906fb5685Smark }
1270