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