(no message)
[unix-history] / usr / src / usr.bin / ex / ex_cmdsub.c
CommitLineData
299f2784
MH
1/* Copyright (c) 1981 Regents of the University of California */
2static char *sccsid = "@(#)ex_cmdsub.c 7.1 %G%";
4beab9c4
MH
3#include "ex.h"
4#include "ex_argv.h"
5#include "ex_temp.h"
6#include "ex_tty.h"
d266c416 7#include "ex_vis.h"
4beab9c4
MH
8
9/*
10 * Command mode subroutines implementing
11 * append, args, copy, delete, join, move, put,
12 * shift, tag, yank, z and undo
13 */
14
15bool endline = 1;
16line *tad1;
d266c416 17static jnoop();
4beab9c4
MH
18
19/*
20 * Append after line a lines returned by function f.
21 * Be careful about intermediate states to avoid scramble
22 * if an interrupt comes in.
23 */
24append(f, a)
25 int (*f)();
26 line *a;
27{
28 register line *a1, *a2, *rdot;
29 int nline;
30
31 nline = 0;
32 dot = a;
887e3e0d 33 if(FIXUNDO && !inopen && f!=getsub) {
4beab9c4
MH
34 undap1 = undap2 = dot + 1;
35 undkind = UNDCHANGE;
36 }
37 while ((*f)() == 0) {
38 if (truedol >= endcore) {
39 if (morelines() < 0) {
887e3e0d 40 if (FIXUNDO && f == getsub) {
4beab9c4
MH
41 undap1 = addr1;
42 undap2 = addr2 + 1;
43 }
44 error("Out of memory@- too many lines in file");
45 }
46 }
47 nline++;
48 a1 = truedol + 1;
49 a2 = a1 + 1;
50 dot++;
51 undap2++;
52 dol++;
53 unddol++;
54 truedol++;
55 for (rdot = dot; a1 > rdot;)
56 *--a2 = *--a1;
57 *rdot = 0;
58 putmark(rdot);
59 if (f == gettty) {
60 dirtcnt++;
61 TSYNC();
62 }
63 }
64 return (nline);
65}
66
67appendnone()
68{
69
887e3e0d 70 if(FIXUNDO) {
4beab9c4
MH
71 undkind = UNDCHANGE;
72 undap1 = undap2 = addr1;
73 }
74}
75
76/*
77 * Print out the argument list, with []'s around the current name.
78 */
79pargs()
80{
81 register char **av = argv0, *as = args0;
82 register int ac;
83
84 for (ac = 0; ac < argc0; ac++) {
85 if (ac != 0)
86 putchar(' ');
87 if (ac + argc == argc0 - 1)
88 printf("[");
89 lprintf("%s", as);
90 if (ac + argc == argc0 - 1)
91 printf("]");
92 as = av ? *++av : strend(as) + 1;
93 }
94 noonl();
95}
96
97/*
98 * Delete lines; two cases are if we are really deleting,
99 * more commonly we are just moving lines to the undo save area.
100 */
101delete(hush)
102 bool hush;
103{
104 register line *a1, *a2;
105
106 nonzero();
887e3e0d 107 if(FIXUNDO) {
4beab9c4
MH
108 register int (*dsavint)();
109
04379bab
MH
110#ifdef TRACE
111 if (trace)
112 vudump("before delete");
113#endif
4beab9c4
MH
114 change();
115 dsavint = signal(SIGINT, SIG_IGN);
116 undkind = UNDCHANGE;
117 a1 = addr1;
118 squish();
119 a2 = addr2;
120 if (a2++ != dol) {
121 reverse(a1, a2);
122 reverse(a2, dol + 1);
123 reverse(a1, dol + 1);
124 }
125 dol -= a2 - a1;
126 unddel = a1 - 1;
127 if (a1 > dol)
128 a1 = dol;
129 dot = a1;
130 pkill[0] = pkill[1] = 0;
131 signal(SIGINT, dsavint);
04379bab
MH
132#ifdef TRACE
133 if (trace)
134 vudump("after delete");
135#endif
4beab9c4
MH
136 } else {
137 register line *a3;
138 register int i;
139
140 change();
141 a1 = addr1;
142 a2 = addr2 + 1;
143 a3 = truedol;
144 i = a2 - a1;
145 unddol -= i;
146 undap2 -= i;
147 dol -= i;
148 truedol -= i;
149 do
150 *a1++ = *a2++;
151 while (a2 <= a3);
152 a1 = addr1;
153 if (a1 > dol)
154 a1 = dol;
155 dot = a1;
156 }
157 if (!hush)
158 killed();
159}
160
161deletenone()
162{
163
887e3e0d 164 if(FIXUNDO) {
4beab9c4
MH
165 undkind = UNDCHANGE;
166 squish();
167 unddel = addr1;
168 }
169}
170
171/*
172 * Crush out the undo save area, moving the open/visual
173 * save area down in its place.
174 */
175squish()
176{
177 register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
178
887e3e0d
MH
179 if(FIXUNDO) {
180 if (inopen == -1)
181 return;
182 if (a1 < a2 && a2 < a3)
183 do
184 *a1++ = *a2++;
185 while (a2 < a3);
186 truedol -= unddol - dol;
187 unddol = dol;
188 }
4beab9c4
MH
189}
190
191/*
192 * Join lines. Special hacks put in spaces, two spaces if
193 * preceding line ends with '.', or no spaces if next line starts with ).
194 */
195static int jcount, jnoop();
196
197join(c)
198 int c;
199{
200 register line *a1;
201 register char *cp, *cp1;
202
203 cp = genbuf;
204 *cp = 0;
205 for (a1 = addr1; a1 <= addr2; a1++) {
206 getline(*a1);
207 cp1 = linebuf;
208 if (a1 != addr1 && c == 0) {
209 while (*cp1 == ' ' || *cp1 == '\t')
210 cp1++;
211 if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
212 if (*cp1 != ')') {
213 *cp++ = ' ';
214 if (cp[-2] == '.')
215 *cp++ = ' ';
216 }
217 }
218 }
219 while (*cp++ = *cp1++)
220 if (cp > &genbuf[LBSIZE-2])
221 error("Line overflow|Result line of join would be too long");
222 cp--;
223 }
224 strcLIN(genbuf);
225 delete(0);
226 jcount = 1;
d266c416
MH
227 if (FIXUNDO)
228 undap1 = undap2 = addr1;
4beab9c4 229 ignore(append(jnoop, --addr1));
d266c416
MH
230 if (FIXUNDO)
231 vundkind = VMANY;
4beab9c4
MH
232}
233
234static
235jnoop()
236{
237
238 return(--jcount);
239}
240
241/*
242 * Move and copy lines. Hard work is done by move1 which
243 * is also called by undo.
244 */
245int getcopy();
246
247move()
248{
249 register line *adt;
250 bool iscopy = 0;
251
252 if (Command[0] == 'm') {
253 setdot1();
254 markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
255 } else {
256 iscopy++;
257 setdot();
258 }
259 nonzero();
299f2784 260 adt = address((char*)0);
4beab9c4
MH
261 if (adt == 0)
262 serror("%s where?|%s requires a trailing address", Command);
263 newline();
264 move1(iscopy, adt);
265 killed();
266}
267
268move1(cflag, addrt)
269 int cflag;
270 line *addrt;
271{
272 register line *adt, *ad1, *ad2;
273 int lines;
274
275 adt = addrt;
276 lines = (addr2 - addr1) + 1;
277 if (cflag) {
278 tad1 = addr1;
279 ad1 = dol;
280 ignore(append(getcopy, ad1++));
281 ad2 = dol;
282 } else {
283 ad2 = addr2;
284 for (ad1 = addr1; ad1 <= ad2;)
285 *ad1++ &= ~01;
286 ad1 = addr1;
287 }
288 ad2++;
289 if (adt < ad1) {
290 if (adt + 1 == ad1 && !cflag && !inglobal)
291 error("That move would do nothing!");
292 dot = adt + (ad2 - ad1);
293 if (++adt != ad1) {
294 reverse(adt, ad1);
295 reverse(ad1, ad2);
296 reverse(adt, ad2);
297 }
298 } else if (adt >= ad2) {
299 dot = adt++;
300 reverse(ad1, ad2);
301 reverse(ad2, adt);
302 reverse(ad1, adt);
303 } else
304 error("Move to a moved line");
305 change();
306 if (!inglobal)
887e3e0d
MH
307 if(FIXUNDO) {
308 if (cflag) {
309 undap1 = addrt + 1;
310 undap2 = undap1 + lines;
311 deletenone();
312 } else {
313 undkind = UNDMOVE;
314 undap1 = addr1;
315 undap2 = addr2;
316 unddel = addrt;
317 squish();
318 }
4beab9c4
MH
319 }
320}
321
322getcopy()
323{
324
325 if (tad1 > addr2)
326 return (EOF);
327 getline(*tad1++);
328 return (0);
329}
330
331/*
332 * Put lines in the buffer from the undo save area.
333 */
334getput()
335{
336
337 if (tad1 > unddol)
338 return (EOF);
339 getline(*tad1++);
340 tad1++;
341 return (0);
342}
343
344put()
345{
346 register int cnt;
347
887e3e0d
MH
348 if (!FIXUNDO)
349 error("Cannot put inside global/macro");
4beab9c4
MH
350 cnt = unddol - dol;
351 if (cnt && inopen && pkill[0] && pkill[1]) {
352 pragged(1);
353 return;
354 }
355 tad1 = dol + 1;
356 ignore(append(getput, addr2));
357 undkind = UNDPUT;
358 notecnt = cnt;
359 netchange(cnt);
360}
361
362/*
363 * A tricky put, of a group of lines in the middle
364 * of an existing line. Only from open/visual.
365 * Argument says pkills have meaning, e.g. called from
366 * put; it is 0 on calls from putreg.
367 */
368pragged(kill)
369 bool kill;
370{
371 extern char *cursor;
372 register char *gp = &genbuf[cursor - linebuf];
373
374 /*
375 * This kind of stuff is TECO's forte.
376 * We just grunge along, since it cuts
377 * across our line-oriented model of the world
378 * almost scrambling our addled brain.
379 */
380 if (!kill)
381 getDOT();
382 strcpy(genbuf, linebuf);
383 getline(*unddol);
384 if (kill)
385 *pkill[1] = 0;
386 strcat(linebuf, gp);
387 putmark(unddol);
388 getline(dol[1]);
389 if (kill)
390 strcLIN(pkill[0]);
391 strcpy(gp, linebuf);
392 strcLIN(genbuf);
393 putmark(dol+1);
394 undkind = UNDCHANGE;
395 undap1 = dot;
396 undap2 = dot + 1;
397 unddel = dot - 1;
398 undo(1);
399}
400
401/*
402 * Shift lines, based on c.
403 * If c is neither < nor >, then this is a lisp aligning =.
404 */
405shift(c, cnt)
406 int c;
407 int cnt;
408{
409 register line *addr;
410 register char *cp;
411 char *dp;
412 register int i;
413
887e3e0d 414 if(FIXUNDO)
4beab9c4
MH
415 save12(), undkind = UNDCHANGE;
416 cnt *= value(SHIFTWIDTH);
417 for (addr = addr1; addr <= addr2; addr++) {
418 dot = addr;
419#ifdef LISPCODE
420 if (c == '=' && addr == addr1 && addr != addr2)
421 continue;
422#endif
423 getDOT();
424 i = whitecnt(linebuf);
425 switch (c) {
426
427 case '>':
428 if (linebuf[0] == 0)
429 continue;
430 cp = genindent(i + cnt);
431 break;
432
433 case '<':
434 if (i == 0)
435 continue;
436 i -= cnt;
437 cp = i > 0 ? genindent(i) : genbuf;
438 break;
439
440#ifdef LISPCODE
441 default:
442 i = lindent(addr);
443 getDOT();
444 cp = genindent(i);
445 break;
446#endif
447 }
448 if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
449 error("Line too long|Result line after shift would be too long");
450 CP(cp, dp);
451 strcLIN(genbuf);
452 putmark(addr);
453 }
454 killed();
455}
456
457/*
458 * Find a tag in the tags file.
459 * Most work here is in parsing the tags file itself.
460 */
461tagfind(quick)
462 bool quick;
463{
464 char cmdbuf[BUFSIZ];
465 char filebuf[FNSIZE];
887e3e0d 466 char tagfbuf[128];
4beab9c4
MH
467 register int c, d;
468 bool samef = 1;
887e3e0d
MH
469 int tfcount = 0;
470 int omagic;
471 char *fn, *fne;
299f2784 472#ifdef STDIO /* mjm: was VMUNIX */
887e3e0d
MH
473 /*
474 * We have lots of room so we bring in stdio and do
475 * a binary search on the tags file.
476 */
477# undef EOF
478# include <stdio.h>
479# undef getchar
480# undef putchar
481 FILE *iof;
482 char iofbuf[BUFSIZ];
483 long mid; /* assumed byte offset */
484 long top, bot; /* length of tag file */
485 struct stat sbuf;
486#endif
4beab9c4
MH
487
488 omagic = value(MAGIC);
4beab9c4
MH
489 if (!skipend()) {
490 register char *lp = lasttag;
491
492 while (!iswhite(peekchar()) && !endcmd(peekchar()))
493 if (lp < &lasttag[sizeof lasttag - 2])
494 *lp++ = getchar();
495 else
496 ignchar();
497 *lp++ = 0;
498 if (!endcmd(peekchar()))
499badtag:
500 error("Bad tag|Give one tag per line");
501 } else if (lasttag[0] == 0)
502 error("No previous tag");
503 c = getchar();
504 if (!endcmd(c))
505 goto badtag;
506 if (c == EOF)
507 ungetchar(c);
508 clrstats();
887e3e0d
MH
509
510 /*
511 * Loop once for each file in tags "path".
512 */
513 CP(tagfbuf, svalue(TAGS));
514 fne = tagfbuf - 1;
515 while (fne) {
516 fn = ++fne;
517 while (*fne && *fne != ' ')
518 fne++;
519 if (*fne == 0)
520 fne = 0; /* done, quit after this time */
521 else
522 *fne = 0; /* null terminate filename */
299f2784 523#ifdef STDIO /* mjm: was VMUNIX */
887e3e0d
MH
524 iof = fopen(fn, "r");
525 if (iof == NULL)
526 continue;
527 tfcount++;
528 setbuf(iof, iofbuf);
529 fstat(fileno(iof), &sbuf);
530 top = sbuf.st_size;
531 if (top == 0L || iof == NULL)
532 top = -1L;
533 bot = 0L;
534 while (top >= bot) {
535#else
536 /*
537 * Avoid stdio and scan tag file linearly.
538 */
539 io = open(fn, 0);
540 if (io<0)
541 continue;
d266c416 542 tfcount++;
4beab9c4 543 while (getfile() == 0) {
887e3e0d
MH
544#endif
545 /* loop for each tags file entry */
4beab9c4
MH
546 register char *cp = linebuf;
547 register char *lp = lasttag;
548 char *oglobp;
549
299f2784 550#ifdef STDIO /* mjm: was VMUNIX */
887e3e0d
MH
551 mid = (top + bot) / 2;
552 fseek(iof, mid, 0);
553 if (mid > 0) /* to get first tag in file to work */
04379bab
MH
554 /* scan to next \n */
555 if(fgets(linebuf, sizeof linebuf, iof)==NULL)
556 goto goleft;
557 /* get the line itself */
558 if(fgets(linebuf, sizeof linebuf, iof)==NULL)
559 goto goleft;
887e3e0d
MH
560 linebuf[strlen(linebuf)-1] = 0; /* was '\n' */
561#endif
4beab9c4
MH
562 while (*cp && *lp == *cp)
563 cp++, lp++;
d266c416 564 if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 || lp-lasttag < value(TAGLENGTH))) {
299f2784 565#ifdef STDIO /* mjm: was VMUNIX */
887e3e0d
MH
566 if (*lp > *cp)
567 bot = mid + 1;
568 else
04379bab 569goleft:
887e3e0d
MH
570 top = mid - 1;
571#endif
572 /* Not this tag. Try the next */
4beab9c4 573 continue;
887e3e0d
MH
574 }
575
576 /*
577 * We found the tag. Decode the line in the file.
578 */
299f2784 579#ifdef STDIO /* mjm: was VMUNIX */
887e3e0d
MH
580 fclose(iof);
581#else
4beab9c4 582 close(io);
887e3e0d 583#endif
d266c416
MH
584 /* Rest of tag if abbreviated */
585 while (!iswhite(*cp))
586 cp++;
587
887e3e0d 588 /* name of file */
4beab9c4
MH
589 while (*cp && iswhite(*cp))
590 cp++;
591 if (!*cp)
592badtags:
593 serror("%s: Bad tags file entry", lasttag);
594 lp = filebuf;
595 while (*cp && *cp != ' ' && *cp != '\t') {
596 if (lp < &filebuf[sizeof filebuf - 2])
597 *lp++ = *cp;
598 cp++;
599 }
600 *lp++ = 0;
887e3e0d 601
4beab9c4
MH
602 if (*cp == 0)
603 goto badtags;
604 if (dol != zero) {
605 /*
606 * Save current position in 't for ^^ in visual.
607 */
608 names['t'-'a'] = *dot &~ 01;
609 if (inopen) {
d266c416 610 extern char *ncols['z'-'a'+2];
4beab9c4
MH
611 extern char *cursor;
612
613 ncols['t'-'a'] = cursor;
614 }
615 }
616 strcpy(cmdbuf, cp);
617 if (strcmp(filebuf, savedfile) || !edited) {
618 char cmdbuf2[sizeof filebuf + 10];
619
887e3e0d 620 /* Different file. Do autowrite & get it. */
4beab9c4
MH
621 if (!quick) {
622 ckaw();
623 if (chng && dol > zero)
624 error("No write@since last change (:tag! overrides)");
625 }
626 oglobp = globp;
627 strcpy(cmdbuf2, "e! ");
628 strcat(cmdbuf2, filebuf);
629 globp = cmdbuf2;
630 d = peekc; ungetchar(0);
631 commands(1, 1);
632 peekc = d;
633 globp = oglobp;
44232d5b 634 value(MAGIC) = omagic;
4beab9c4
MH
635 samef = 0;
636 }
887e3e0d
MH
637
638 /*
639 * Look for pattern in the current file.
640 */
4beab9c4
MH
641 oglobp = globp;
642 globp = cmdbuf;
643 d = peekc; ungetchar(0);
644 if (samef)
645 markpr(dot);
887e3e0d
MH
646 /*
647 * BUG: if it isn't found (user edited header
648 * line) we get left in nomagic mode.
649 */
44232d5b 650 value(MAGIC) = 0;
4beab9c4
MH
651 commands(1, 1);
652 peekc = d;
653 globp = oglobp;
654 value(MAGIC) = omagic;
655 return;
887e3e0d
MH
656 } /* end of "for each tag in file" */
657
658 /*
659 * No such tag in this file. Close it and try the next.
660 */
299f2784 661#ifdef STDIO /* mjm: was VMUNIX */
887e3e0d
MH
662 fclose(iof);
663#else
664 close(io);
665#endif
666 } /* end of "for each file in path" */
667 if (tfcount <= 0)
4beab9c4 668 error("No tags file");
887e3e0d
MH
669 else
670 serror("%s: No such tag@in tags file", lasttag);
4beab9c4
MH
671}
672
673/*
674 * Save lines from addr1 thru addr2 as though
675 * they had been deleted.
676 */
677yank()
678{
679
887e3e0d
MH
680 if (!FIXUNDO)
681 error("Can't yank inside global/macro");
4beab9c4
MH
682 save12();
683 undkind = UNDNONE;
684 killcnt(addr2 - addr1 + 1);
685}
686
687/*
688 * z command; print windows of text in the file.
689 *
690 * If this seems unreasonably arcane, the reasons
691 * are historical. This is one of the first commands
692 * added to the first ex (then called en) and the
693 * number of facilities here were the major advantage
694 * of en over ed since they allowed more use to be
695 * made of fast terminals w/o typing .,.22p all the time.
696 */
697bool zhadpr;
698bool znoclear;
699short zweight;
700
701zop(hadpr)
702 int hadpr;
703{
704 register int c, lines, op;
705 bool excl;
706
707 zhadpr = hadpr;
708 notempty();
709 znoclear = 0;
710 zweight = 0;
711 excl = exclam();
712 switch (c = op = getchar()) {
713
714 case '^':
715 zweight = 1;
716 case '-':
717 case '+':
718 while (peekchar() == op) {
719 ignchar();
720 zweight++;
721 }
722 case '=':
723 case '.':
724 c = getchar();
725 break;
726
727 case EOF:
728 znoclear++;
729 break;
730
731 default:
732 op = 0;
733 break;
734 }
735 if (isdigit(c)) {
736 lines = c - '0';
737 for(;;) {
738 c = getchar();
739 if (!isdigit(c))
740 break;
741 lines *= 10;
742 lines += c - '0';
743 }
744 if (lines < LINES)
745 znoclear++;
746 value(WINDOW) = lines;
747 if (op == '=')
748 lines += 2;
749 } else
750 lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL);
751 if (inopen || c != EOF) {
752 ungetchar(c);
753 newline();
754 }
755 addr1 = addr2;
756 if (addr2 == 0 && dot < dol && op == 0)
757 addr1 = addr2 = dot+1;
758 setdot();
759 zop2(lines, op);
760}
761
762zop2(lines, op)
763 register int lines;
764 register int op;
765{
766 register line *split;
767
768 split = NULL;
769 switch (op) {
770
771 case EOF:
772 if (addr2 == dol)
773 error("\nAt EOF");
774 case '+':
775 if (addr2 == dol)
776 error("At EOF");
777 addr2 += lines * zweight;
778 if (addr2 > dol)
779 error("Hit BOTTOM");
780 addr2++;
781 default:
782 addr1 = addr2;
783 addr2 += lines-1;
784 dot = addr2;
785 break;
786
787 case '=':
788 case '.':
789 znoclear = 0;
790 lines--;
791 lines >>= 1;
792 if (op == '=')
793 lines--;
794 addr1 = addr2 - lines;
795 if (op == '=')
796 dot = split = addr2;
797 addr2 += lines;
798 if (op == '.') {
799 markDOT();
800 dot = addr2;
801 }
802 break;
803
804 case '^':
805 case '-':
806 addr2 -= lines * zweight;
807 if (addr2 < one)
808 error("Hit TOP");
809 lines--;
810 addr1 = addr2 - lines;
811 dot = addr2;
812 break;
813 }
814 if (addr1 <= zero)
815 addr1 = one;
816 if (addr2 > dol)
817 addr2 = dol;
818 if (dot > dol)
819 dot = dol;
820 if (addr1 > addr2)
821 return;
822 if (op == EOF && zhadpr) {
823 getline(*addr1);
824 putchar('\r' | QUOTE);
825 shudclob = 1;
826 } else if (znoclear == 0 && CL != NOSTR && !inopen) {
827 flush1();
828 vclear();
829 }
830 if (addr2 - addr1 > 1)
831 pstart();
832 if (split) {
833 plines(addr1, split - 1, 0);
834 splitit();
835 plines(split, split, 0);
836 splitit();
837 addr1 = split + 1;
838 }
839 plines(addr1, addr2, 0);
840}
841
842static
843splitit()
844{
845 register int l;
846
847 for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
848 putchar('-');
849 putnl();
850}
851
852plines(adr1, adr2, movedot)
853 line *adr1;
854 register line *adr2;
855 bool movedot;
856{
857 register line *addr;
858
859 pofix();
860 for (addr = adr1; addr <= adr2; addr++) {
861 getline(*addr);
862 pline(lineno(addr));
863 if (inopen)
864 putchar('\n' | QUOTE);
865 if (movedot)
866 dot = addr;
867 }
868}
869
870pofix()
871{
872
873 if (inopen && Outchar != termchar) {
874 vnfl();
875 setoutt();
876 }
877}
878
879/*
880 * Dudley doright to the rescue.
881 * Undo saves the day again.
882 * A tip of the hatlo hat to Warren Teitleman
883 * who made undo as useful as do.
884 *
885 * Command level undo works easily because
886 * the editor has a unique temporary file
887 * index for every line which ever existed.
888 * We don't have to save large blocks of text,
889 * only the indices which are small. We do this
890 * by moving them to after the last line in the
891 * line buffer array, and marking down info
892 * about whence they came.
893 *
894 * Undo is its own inverse.
895 */
896undo(c)
897 bool c;
898{
899 register int i;
900 register line *jp, *kp;
901 line *dolp1, *newdol, *newadot;
902
887e3e0d
MH
903#ifdef TRACE
904 if (trace)
905 vudump("before undo");
906#endif
4beab9c4
MH
907 if (inglobal && inopen <= 0)
908 error("Can't undo in global@commands");
909 if (!c)
910 somechange();
911 pkill[0] = pkill[1] = 0;
912 change();
913 if (undkind == UNDMOVE) {
914 /*
915 * Command to be undone is a move command.
916 * This is handled as a special case by noting that
917 * a move "a,b m c" can be inverted by another move.
918 */
919 if ((i = (jp = unddel) - undap2) > 0) {
920 /*
921 * when c > b inverse is a+(c-b),c m a-1
922 */
923 addr2 = jp;
924 addr1 = (jp = undap1) + i;
925 unddel = jp-1;
926 } else {
927 /*
928 * when b > c inverse is c+1,c+1+(b-a) m b
929 */
930 addr1 = ++jp;
931 addr2 = jp + ((unddel = undap2) - undap1);
932 }
933 kp = undap1;
934 move1(0, unddel);
935 dot = kp;
936 Command = "move";
937 killed();
938 } else {
939 int cnt;
940
941 newadot = dot;
942 cnt = lineDOL();
943 newdol = dol;
944 dolp1 = dol + 1;
945 /*
946 * Command to be undone is a non-move.
947 * All such commands are treated as a combination of
948 * a delete command and a append command.
949 * We first move the lines appended by the last command
950 * from undap1 to undap2-1 so that they are just before the
951 * saved deleted lines.
952 */
953 if ((i = (kp = undap2) - (jp = undap1)) > 0) {
954 if (kp != dolp1) {
955 reverse(jp, kp);
956 reverse(kp, dolp1);
957 reverse(jp, dolp1);
958 }
959 /*
960 * Account for possible backward motion of target
961 * for restoration of saved deleted lines.
962 */
963 if (unddel >= jp)
964 unddel -= i;
965 newdol -= i;
966 /*
967 * For the case where no lines are restored, dot
968 * is the line before the first line deleted.
969 */
970 dot = jp-1;
971 }
972 /*
973 * Now put the deleted lines, if any, back where they were.
974 * Basic operation is: dol+1,unddol m unddel
975 */
976 if (undkind == UNDPUT) {
977 unddel = undap1 - 1;
978 squish();
979 }
980 jp = unddel + 1;
981 if ((i = (kp = unddol) - dol) > 0) {
982 if (jp != dolp1) {
983 reverse(jp, dolp1);
984 reverse(dolp1, ++kp);
985 reverse(jp, kp);
986 }
987 /*
988 * Account for possible forward motion of the target
989 * for restoration of the deleted lines.
990 */
991 if (undap1 >= jp)
992 undap1 += i;
993 /*
994 * Dot is the first resurrected line.
995 */
996 dot = jp;
997 newdol += i;
998 }
999 /*
1000 * Clean up so we are invertible
1001 */
1002 unddel = undap1 - 1;
1003 undap1 = jp;
1004 undap2 = jp + i;
1005 dol = newdol;
1006 netchHAD(cnt);
1007 if (undkind == UNDALL) {
1008 dot = undadot;
1009 undadot = newadot;
887e3e0d
MH
1010 } else
1011 undkind = UNDCHANGE;
4beab9c4 1012 }
d266c416
MH
1013 /*
1014 * Defensive programming - after a munged undadot.
1015 * Also handle empty buffer case.
1016 */
1017 if ((dot <= zero || dot > dol) && dot != dol)
4beab9c4 1018 dot = one;
887e3e0d
MH
1019#ifdef TRACE
1020 if (trace)
1021 vudump("after undo");
1022#endif
4beab9c4
MH
1023}
1024
1025/*
1026 * Be (almost completely) sure there really
1027 * was a change, before claiming to undo.
1028 */
1029somechange()
1030{
1031 register line *ip, *jp;
1032
1033 switch (undkind) {
1034
1035 case UNDMOVE:
1036 return;
1037
1038 case UNDCHANGE:
1039 if (undap1 == undap2 && dol == unddol)
1040 break;
1041 return;
1042
1043 case UNDPUT:
1044 if (undap1 != undap2)
1045 return;
1046 break;
1047
1048 case UNDALL:
1049 if (unddol - dol != lineDOL())
1050 return;
1051 for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
1052 if ((*ip &~ 01) != (*jp &~ 01))
1053 return;
1054 break;
1055
1056 case UNDNONE:
1057 error("Nothing to undo");
1058 }
1059 error("Nothing changed|Last undoable command didn't change anything");
1060}
1061
1062/*
1063 * Map command:
1064 * map src dest
1065 */
d266c416 1066mapcmd(un, ab)
4beab9c4 1067 int un; /* true if this is unmap command */
d266c416 1068 int ab; /* true if this is abbr command */
4beab9c4 1069{
d266c416 1070 char lhs[100], rhs[100]; /* max sizes resp. */
4beab9c4 1071 register char *p;
299f2784 1072 register int c; /* mjm: char --> int */
4beab9c4 1073 char *dname;
887e3e0d 1074 struct maps *mp; /* the map structure we are working on */
4beab9c4 1075
d266c416 1076 mp = ab ? abbrevs : exclam() ? immacs : arrows;
4beab9c4
MH
1077 if (skipend()) {
1078 int i;
1079
1080 /* print current mapping values */
1081 if (peekchar() != EOF)
1082 ignchar();
d266c416
MH
1083 if (un)
1084 error("Missing lhs");
4beab9c4
MH
1085 if (inopen)
1086 pofix();
887e3e0d
MH
1087 for (i=0; mp[i].mapto; i++)
1088 if (mp[i].cap) {
1089 lprintf("%s", mp[i].descr);
4beab9c4 1090 putchar('\t');
887e3e0d 1091 lprintf("%s", mp[i].cap);
4beab9c4 1092 putchar('\t');
887e3e0d 1093 lprintf("%s", mp[i].mapto);
4beab9c4
MH
1094 putNFL();
1095 }
1096 return;
1097 }
1098
1099 ignore(skipwh());
1100 for (p=lhs; ; ) {
1101 c = getchar();
1102 if (c == CTRL(v)) {
1103 c = getchar();
d266c416
MH
1104 } else if (!un && any(c, " \t")) {
1105 /* End of lhs */
1106 break;
1107 } else if (endcmd(c) && c!='"') {
4beab9c4
MH
1108 ungetchar(c);
1109 if (un) {
1110 newline();
d266c416 1111 *p = 0;
887e3e0d 1112 addmac(lhs, NOSTR, NOSTR, mp);
4beab9c4
MH
1113 return;
1114 } else
1115 error("Missing rhs");
1116 }
1117 *p++ = c;
1118 }
1119 *p = 0;
1120
1121 if (skipend())
1122 error("Missing rhs");
1123 for (p=rhs; ; ) {
1124 c = getchar();
1125 if (c == CTRL(v)) {
1126 c = getchar();
d266c416 1127 } else if (endcmd(c) && c!='"') {
4beab9c4
MH
1128 ungetchar(c);
1129 break;
1130 }
1131 *p++ = c;
1132 }
1133 *p = 0;
1134 newline();
1135 /*
1136 * Special hack for function keys: #1 means key f1, etc.
1137 * If the terminal doesn't have function keys, we just use #1.
1138 */
1139 if (lhs[0] == '#') {
1140 char *fnkey;
1141 char *fkey();
1142 char funkey[3];
1143
1144 fnkey = fkey(lhs[1] - '0');
1145 funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
1146 if (fnkey)
1147 strcpy(lhs, fnkey);
1148 dname = funkey;
1149 } else {
1150 dname = lhs;
1151 }
887e3e0d 1152 addmac(lhs,rhs,dname,mp);
4beab9c4
MH
1153}
1154
1155/*
1156 * Add a macro definition to those that already exist. The sequence of
1157 * chars "src" is mapped into "dest". If src is already mapped into something
1158 * this overrides the mapping. There is no recursion. Unmap is done by
887e3e0d
MH
1159 * using NOSTR for dest. Dname is what to show in listings. mp is
1160 * the structure to affect (arrows, etc).
4beab9c4 1161 */
887e3e0d 1162addmac(src,dest,dname,mp)
4beab9c4 1163 register char *src, *dest, *dname;
887e3e0d 1164 register struct maps *mp;
4beab9c4
MH
1165{
1166 register int slot, zer;
1167
d266c416
MH
1168#ifdef TRACE
1169 if (trace)
1170 fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
1171#endif
887e3e0d 1172 if (dest && mp==arrows) {
4beab9c4
MH
1173 /* Make sure user doesn't screw himself */
1174 /*
44232d5b
MH
1175 * Prevent tail recursion. We really should be
1176 * checking to see if src is a suffix of dest
887e3e0d
MH
1177 * but this makes mapping involving escapes that
1178 * is reasonable mess up.
4beab9c4 1179 */
44232d5b
MH
1180 if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
1181 error("No tail recursion");
4beab9c4
MH
1182 /*
1183 * We don't let the user rob himself of ":", and making
1184 * multi char words is a bad idea so we don't allow it.
1185 * Note that if user sets mapinput and maps all of return,
1186 * linefeed, and escape, he can screw himself. This is
1187 * so weird I don't bother to check for it.
1188 */
1189 if (isalpha(src[0]) && src[1] || any(src[0],":"))
1190 error("Too dangerous to map that");
4beab9c4 1191 }
887e3e0d
MH
1192 else if (dest) {
1193 /* check for tail recursion in input mode: fussier */
1194 if (eq(src, dest+strlen(dest)-strlen(src)))
1195 error("No tail recursion");
1196 }
1197 /*
1198 * If the src were null it would cause the dest to
1199 * be mapped always forever. This is not good.
1200 */
1201 if (src == NOSTR || src[0] == 0)
1202 error("Missing lhs");
4beab9c4
MH
1203
1204 /* see if we already have a def for src */
1205 zer = -1;
887e3e0d
MH
1206 for (slot=0; mp[slot].mapto; slot++) {
1207 if (mp[slot].cap) {
d266c416 1208 if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
4beab9c4
MH
1209 break; /* if so, reuse slot */
1210 } else {
1211 zer = slot; /* remember an empty slot */
1212 }
1213 }
1214
1215 if (dest == NOSTR) {
1216 /* unmap */
887e3e0d
MH
1217 if (mp[slot].cap) {
1218 mp[slot].cap = NOSTR;
1219 mp[slot].descr = NOSTR;
4beab9c4
MH
1220 } else {
1221 error("Not mapped|That macro wasn't mapped");
1222 }
1223 return;
1224 }
1225
1226 /* reuse empty slot, if we found one and src isn't already defined */
887e3e0d 1227 if (zer >= 0 && mp[slot].mapto == 0)
4beab9c4
MH
1228 slot = zer;
1229
1230 /* if not, append to end */
1231 if (slot >= MAXNOMACS)
1232 error("Too many macros");
1233 if (msnext == 0) /* first time */
1234 msnext = mapspace;
1235 /* Check is a bit conservative, we charge for dname even if reusing src */
1236 if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS)
1237 error("Too much macro text");
1238 CP(msnext, src);
887e3e0d 1239 mp[slot].cap = msnext;
4beab9c4
MH
1240 msnext += strlen(src) + 1; /* plus 1 for null on the end */
1241 CP(msnext, dest);
887e3e0d 1242 mp[slot].mapto = msnext;
4beab9c4
MH
1243 msnext += strlen(dest) + 1;
1244 if (dname) {
1245 CP(msnext, dname);
887e3e0d 1246 mp[slot].descr = msnext;
4beab9c4
MH
1247 msnext += strlen(dname) + 1;
1248 } else {
1249 /* default descr to string user enters */
887e3e0d 1250 mp[slot].descr = src;
4beab9c4
MH
1251 }
1252}
1253
1254/*
1255 * Implements macros from command mode. c is the buffer to
1256 * get the macro from.
1257 */
1258cmdmac(c)
1259char c;
1260{
1261 char macbuf[BUFSIZ];
1262 line *ad, *a1, *a2;
1263 char *oglobp;
1264 char pk;
1265 bool oinglobal;
1266
1267 lastmac = c;
1268 oglobp = globp;
1269 oinglobal = inglobal;
1270 pk = peekc; peekc = 0;
1271 if (inglobal < 2)
1272 inglobal = 1;
1273 regbuf(c, macbuf, sizeof(macbuf));
1274 a1 = addr1; a2 = addr2;
1275 for (ad=a1; ad<=a2; ad++) {
1276 globp = macbuf;
1277 dot = ad;
1278 commands(1,1);
1279 }
1280 globp = oglobp;
1281 inglobal = oinglobal;
1282 peekc = pk;
1283}