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