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