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