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