release 3.2, Jan 4, 1980
[unix-history] / usr / src / usr.bin / ex / ex_cmdsub.c
CommitLineData
4beab9c4
MH
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
13bool endline = 1;
14line *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 */
21append(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
71appendnone()
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 */
83pargs()
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 */
105delete(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
157deletenone()
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 */
171squish()
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 */
189static int jcount, jnoop();
190
191join(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
224static
225jnoop()
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 */
235int getcopy();
236
237move()
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
258move1(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
310getcopy()
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 */
322getput()
323{
324
325 if (tad1 > unddol)
326 return (EOF);
327 getline(*tad1++);
328 tad1++;
329 return (0);
330}
331
332put()
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 */
354pragged(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 */
391shift(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 */
447tagfind(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);
4beab9c4
MH
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()))
469badtag:
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)
496badtags:
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);
44232d5b
MH
533 /*
534 * BUG: if it isn't found (user edited header
535 * line) we get left in nomagic mode.
536 */
537 value(MAGIC) = 0;
4beab9c4
MH
538 commands(1, 1);
539 peekc = d;
540 globp = oglobp;
44232d5b 541 value(MAGIC) = omagic;
4beab9c4
MH
542 samef = 0;
543 }
544 oglobp = globp;
545 globp = cmdbuf;
546 d = peekc; ungetchar(0);
547 if (samef)
548 markpr(dot);
44232d5b 549 value(MAGIC) = 0;
4beab9c4
MH
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 */
566yank()
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 */
584bool zhadpr;
585bool znoclear;
586short zweight;
587
588zop(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
649zop2(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
729static
730splitit()
731{
732 register int l;
733
734 for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
735 putchar('-');
736 putnl();
737}
738
739plines(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
757pofix()
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 */
783undo(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 */
904somechange()
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 */
941mapcmd(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 */
1032addmac(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 /*
44232d5b
MH
1040 * Prevent tail recursion. We really should be
1041 * checking to see if src is a suffix of dest
4beab9c4
MH
1042 * but we are too lazy here, so we don't bother unless
1043 * src is only 1 char long.
1044 */
44232d5b
MH
1045 if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
1046 error("No tail recursion");
4beab9c4
MH
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 */
1118cmdmac(c)
1119char 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}