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