1: /* Copyright (c) 1981 Regents of the University of California */
2: static char *sccsid = "@(#)ex_cmdsub.c 7.1 7/8/81";
3: #include "ex.h"
4: #include "ex_argv.h"
5: #include "ex_temp.h"
6: #include "ex_tty.h"
7: #include "ex_vis.h"
8:
9: /*
10: * Command mode subroutines implementing
11: * append, args, copy, delete, join, move, put,
12: * shift, tag, yank, z and undo
13: */
14:
15: bool endline = 1;
16: line *tad1;
17: static jnoop();
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: */
24: append(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;
33: if(FIXUNDO && !inopen && f!=getsub) {
34: undap1 = undap2 = dot + 1;
35: undkind = UNDCHANGE;
36: }
37: while ((*f)() == 0) {
38: if (truedol >= endcore) {
39: if (morelines() < 0) {
40: if (FIXUNDO && f == getsub) {
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:
67: appendnone()
68: {
69:
70: if(FIXUNDO) {
71: undkind = UNDCHANGE;
72: undap1 = undap2 = addr1;
73: }
74: }
75:
76: /*
77: * Print out the argument list, with []'s around the current name.
78: */
79: pargs()
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: */
101: delete(hush)
102: bool hush;
103: {
104: register line *a1, *a2;
105:
106: nonzero();
107: if(FIXUNDO) {
108: register int (*dsavint)();
109:
110: #ifdef TRACE
111: if (trace)
112: vudump("before delete");
113: #endif
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);
132: #ifdef TRACE
133: if (trace)
134: vudump("after delete");
135: #endif
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:
161: deletenone()
162: {
163:
164: if(FIXUNDO) {
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: */
175: squish()
176: {
177: register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
178:
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: }
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: */
195: static int jcount, jnoop();
196:
197: join(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;
227: if (FIXUNDO)
228: undap1 = undap2 = addr1;
229: ignore(append(jnoop, --addr1));
230: if (FIXUNDO)
231: vundkind = VMANY;
232: }
233:
234: static
235: jnoop()
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: */
245: int getcopy();
246:
247: move()
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();
260: adt = address((char*)0);
261: if (adt == 0)
262: serror("%s where?|%s requires a trailing address", Command);
263: newline();
264: move1(iscopy, adt);
265: killed();
266: }
267:
268: move1(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)
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: }
319: }
320: }
321:
322: getcopy()
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: */
334: getput()
335: {
336:
337: if (tad1 > unddol)
338: return (EOF);
339: getline(*tad1++);
340: tad1++;
341: return (0);
342: }
343:
344: put()
345: {
346: register int cnt;
347:
348: if (!FIXUNDO)
349: error("Cannot put inside global/macro");
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: */
368: pragged(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: */
405: shift(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:
414: if(FIXUNDO)
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: */
461: tagfind(quick)
462: bool quick;
463: {
464: char cmdbuf[BUFSIZ];
465: char filebuf[FNSIZE];
466: char tagfbuf[128];
467: register int c, d;
468: bool samef = 1;
469: int tfcount = 0;
470: int omagic;
471: char *fn, *fne;
472: #ifdef STDIO /* mjm: was VMUNIX */
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
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 STDIO /* mjm: was VMUNIX */
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;
542: tfcount++;
543: while (getfile() == 0) {
544: #endif
545: /* loop for each tags file entry */
546: register char *cp = linebuf;
547: register char *lp = lasttag;
548: char *oglobp;
549:
550: #ifdef STDIO /* mjm: was VMUNIX */
551: mid = (top + bot) / 2;
552: fseek(iof, mid, 0);
553: if (mid > 0) /* to get first tag in file to work */
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;
560: linebuf[strlen(linebuf)-1] = 0; /* was '\n' */
561: #endif
562: while (*cp && *lp == *cp)
563: cp++, lp++;
564: if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 || lp-lasttag < value(TAGLENGTH))) {
565: #ifdef STDIO /* mjm: was VMUNIX */
566: if (*lp > *cp)
567: bot = mid + 1;
568: else
569: goleft:
570: top = mid - 1;
571: #endif
572: /* Not this tag. Try the next */
573: continue;
574: }
575:
576: /*
577: * We found the tag. Decode the line in the file.
578: */
579: #ifdef STDIO /* mjm: was VMUNIX */
580: fclose(iof);
581: #else
582: close(io);
583: #endif
584: /* Rest of tag if abbreviated */
585: while (!iswhite(*cp))
586: cp++;
587:
588: /* name of file */
589: while (*cp && iswhite(*cp))
590: cp++;
591: if (!*cp)
592: badtags:
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;
601:
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) {
610: extern char *ncols['z'-'a'+2];
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:
620: /* Different file. Do autowrite & get it. */
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;
634: value(MAGIC) = omagic;
635: samef = 0;
636: }
637:
638: /*
639: * Look for pattern in the current file.
640: */
641: oglobp = globp;
642: globp = cmdbuf;
643: d = peekc; ungetchar(0);
644: if (samef)
645: markpr(dot);
646: /*
647: * BUG: if it isn't found (user edited header
648: * line) we get left in nomagic mode.
649: */
650: value(MAGIC) = 0;
651: commands(1, 1);
652: peekc = d;
653: globp = oglobp;
654: value(MAGIC) = omagic;
655: return;
656: } /* end of "for each tag in file" */
657:
658: /*
659: * No such tag in this file. Close it and try the next.
660: */
661: #ifdef STDIO /* mjm: was VMUNIX */
662: fclose(iof);
663: #else
664: close(io);
665: #endif
666: } /* end of "for each file in path" */
667: if (tfcount <= 0)
668: error("No tags file");
669: else
670: serror("%s: No such tag@in tags file", lasttag);
671: }
672:
673: /*
674: * Save lines from addr1 thru addr2 as though
675: * they had been deleted.
676: */
677: yank()
678: {
679:
680: if (!FIXUNDO)
681: error("Can't yank inside global/macro");
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: */
697: bool zhadpr;
698: bool znoclear;
699: short zweight;
700:
701: zop(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:
762: zop2(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:
842: static
843: splitit()
844: {
845: register int l;
846:
847: for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
848: putchar('-');
849: putnl();
850: }
851:
852: plines(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:
870: pofix()
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: */
896: undo(c)
897: bool c;
898: {
899: register int i;
900: register line *jp, *kp;
901: line *dolp1, *newdol, *newadot;
902:
903: #ifdef TRACE
904: if (trace)
905: vudump("before undo");
906: #endif
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;
1010: } else
1011: undkind = UNDCHANGE;
1012: }
1013: /*
1014: * Defensive programming - after a munged undadot.
1015: * Also handle empty buffer case.
1016: */
1017: if ((dot <= zero || dot > dol) && dot != dol)
1018: dot = one;
1019: #ifdef TRACE
1020: if (trace)
1021: vudump("after undo");
1022: #endif
1023: }
1024:
1025: /*
1026: * Be (almost completely) sure there really
1027: * was a change, before claiming to undo.
1028: */
1029: somechange()
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: */
1066: mapcmd(un, ab)
1067: int un; /* true if this is unmap command */
1068: int ab; /* true if this is abbr command */
1069: {
1070: char lhs[100], rhs[100]; /* max sizes resp. */
1071: register char *p;
1072: register int c; /* mjm: char --> int */
1073: char *dname;
1074: struct maps *mp; /* the map structure we are working on */
1075:
1076: mp = ab ? abbrevs : exclam() ? immacs : arrows;
1077: if (skipend()) {
1078: int i;
1079:
1080: /* print current mapping values */
1081: if (peekchar() != EOF)
1082: ignchar();
1083: if (un)
1084: error("Missing lhs");
1085: if (inopen)
1086: pofix();
1087: for (i=0; mp[i].mapto; i++)
1088: if (mp[i].cap) {
1089: lprintf("%s", mp[i].descr);
1090: putchar('\t');
1091: lprintf("%s", mp[i].cap);
1092: putchar('\t');
1093: lprintf("%s", mp[i].mapto);
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();
1104: } else if (!un && any(c, " \t")) {
1105: /* End of lhs */
1106: break;
1107: } else if (endcmd(c) && c!='"') {
1108: ungetchar(c);
1109: if (un) {
1110: newline();
1111: *p = 0;
1112: addmac(lhs, NOSTR, NOSTR, mp);
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();
1127: } else if (endcmd(c) && c!='"') {
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: }
1152: addmac(lhs,rhs,dname,mp);
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
1159: * using NOSTR for dest. Dname is what to show in listings. mp is
1160: * the structure to affect (arrows, etc).
1161: */
1162: addmac(src,dest,dname,mp)
1163: register char *src, *dest, *dname;
1164: register struct maps *mp;
1165: {
1166: register int slot, zer;
1167:
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
1172: if (dest && mp==arrows) {
1173: /* Make sure user doesn't screw himself */
1174: /*
1175: * Prevent tail recursion. We really should be
1176: * checking to see if src is a suffix of dest
1177: * but this makes mapping involving escapes that
1178: * is reasonable mess up.
1179: */
1180: if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
1181: error("No tail recursion");
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");
1191: }
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");
1203:
1204: /* see if we already have a def for src */
1205: zer = -1;
1206: for (slot=0; mp[slot].mapto; slot++) {
1207: if (mp[slot].cap) {
1208: if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
1209: break; /* if so, reuse slot */
1210: } else {
1211: zer = slot; /* remember an empty slot */
1212: }
1213: }
1214:
1215: if (dest == NOSTR) {
1216: /* unmap */
1217: if (mp[slot].cap) {
1218: mp[slot].cap = NOSTR;
1219: mp[slot].descr = NOSTR;
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 */
1227: if (zer >= 0 && mp[slot].mapto == 0)
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);
1239: mp[slot].cap = msnext;
1240: msnext += strlen(src) + 1; /* plus 1 for null on the end */
1241: CP(msnext, dest);
1242: mp[slot].mapto = msnext;
1243: msnext += strlen(dest) + 1;
1244: if (dname) {
1245: CP(msnext, dname);
1246: mp[slot].descr = msnext;
1247: msnext += strlen(dname) + 1;
1248: } else {
1249: /* default descr to string user enters */
1250: mp[slot].descr = src;
1251: }
1252: }
1253:
1254: /*
1255: * Implements macros from command mode. c is the buffer to
1256: * get the macro from.
1257: */
1258: cmdmac(c)
1259: char 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: }