1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30
31 /* Copyright (c) 1981 Regents of the University of California */
32
33 #include "ex.h"
34 #include "ex_argv.h"
35 #include "ex_temp.h"
36 #include "ex_tty.h"
37 #include "ex_vis.h"
38 #ifdef STDIO
39 #include <stdio.h>
40 #undef getchar
41 #undef putchar
42 #endif
43
44
45 /*
46 * Command mode subroutines implementing
47 * append, args, copy, delete, join, move, put,
48 * shift, tag, yank, z and undo
49 */
50
51 bool endline = 1;
52 line *tad1;
53 static int jnoop(void);
54 static void splitit(void);
55 int putchar(), getchar();
56 int tags_flag;
57
58 /*
59 * Append after line a lines returned by function f.
60 * Be careful about intermediate states to avoid scramble
61 * if an interrupt comes in.
62 */
63 int
append(int (* f)(),line * a)64 append(int (*f)(), line *a)
65 {
66 line *a1, *a2, *rdot;
67 int nline;
68
69 nline = 0;
70 dot = a;
71 if(FIXUNDO && !inopen && f!=getsub) {
72 undap1 = undap2 = dot + 1;
73 undkind = UNDCHANGE;
74 }
75 while ((*f)() == 0) {
76 if (truedol >= endcore) {
77 if (morelines() < 0) {
78 if (FIXUNDO && f == getsub) {
79 undap1 = addr1;
80 undap2 = addr2 + 1;
81 }
82 error(value(vi_TERSE) ? gettext("Out of memory") :
83 gettext("Out of memory- too many lines in file"));
84 }
85 }
86 nline++;
87 a1 = truedol + 1;
88 a2 = a1 + 1;
89 dot++;
90 undap2++;
91 dol++;
92 unddol++;
93 truedol++;
94 for (rdot = dot; a1 > rdot;)
95 *--a2 = *--a1;
96 *rdot = 0;
97 putmark(rdot);
98 if (f == gettty) {
99 dirtcnt++;
100 TSYNC();
101 }
102 }
103 return (nline);
104 }
105
106 void
appendnone(void)107 appendnone(void)
108 {
109
110 if(FIXUNDO) {
111 undkind = UNDCHANGE;
112 undap1 = undap2 = addr1;
113 }
114 }
115
116 /*
117 * Print out the argument list, with []'s around the current name.
118 */
119 void
pargs(void)120 pargs(void)
121 {
122 unsigned char **av = argv0, *as = args0;
123 int ac;
124
125 for (ac = 0; ac < argc0; ac++) {
126 if (ac != 0)
127 putchar(' ');
128 if (ac + argc == argc0 - 1)
129 viprintf("[");
130 lprintf("%s", as);
131 if (ac + argc == argc0 - 1)
132 viprintf("]");
133 as = av ? *++av : strend(as) + 1;
134 }
135 noonl();
136 }
137
138 /*
139 * Delete lines; two cases are if we are really deleting,
140 * more commonly we are just moving lines to the undo save area.
141 */
142 int
delete(bool hush)143 delete(bool hush)
144 {
145 line *a1, *a2;
146
147 nonzero();
148 if(FIXUNDO) {
149 void (*dsavint)();
150
151 #ifdef UNDOTRACE
152 if (trace)
153 vudump("before delete");
154 #endif
155 change();
156 dsavint = signal(SIGINT, SIG_IGN);
157 undkind = UNDCHANGE;
158 a1 = addr1;
159 squish();
160 a2 = addr2;
161 if (a2++ != dol) {
162 reverse(a1, a2);
163 reverse(a2, dol + 1);
164 reverse(a1, dol + 1);
165 }
166 dol -= a2 - a1;
167 unddel = a1 - 1;
168 if (a1 > dol)
169 a1 = dol;
170 dot = a1;
171 pkill[0] = pkill[1] = 0;
172 signal(SIGINT, dsavint);
173 #ifdef UNDOTRACE
174 if (trace)
175 vudump("after delete");
176 #endif
177 } else {
178 line *a3;
179 int i;
180
181 change();
182 a1 = addr1;
183 a2 = addr2 + 1;
184 a3 = truedol;
185 i = a2 - a1;
186 unddol -= i;
187 undap2 -= i;
188 dol -= i;
189 truedol -= i;
190 do
191 *a1++ = *a2++;
192 while (a2 <= a3);
193 a1 = addr1;
194 if (a1 > dol)
195 a1 = dol;
196 dot = a1;
197 }
198 if (!hush)
199 killed();
200 return (0);
201 }
202
203 void
deletenone(void)204 deletenone(void)
205 {
206
207 if(FIXUNDO) {
208 undkind = UNDCHANGE;
209 squish();
210 unddel = addr1;
211 }
212 }
213
214 /*
215 * Crush out the undo save area, moving the open/visual
216 * save area down in its place.
217 */
218 void
squish(void)219 squish(void)
220 {
221 line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
222
223 if(FIXUNDO) {
224 if (inopen == -1)
225 return;
226 if (a1 < a2 && a2 < a3)
227 do
228 *a1++ = *a2++;
229 while (a2 < a3);
230 truedol -= unddol - dol;
231 unddol = dol;
232 }
233 }
234
235 /*
236 * Join lines. Special hacks put in spaces, two spaces if
237 * preceding line ends with '.', or no spaces if next line starts with ).
238 */
239 static int jcount;
240
241 int
join(int c)242 join(int c)
243 {
244 line *a1;
245 unsigned char *cp, *cp1;
246 #ifndef PRESUNEUC
247 unsigned char *pcp;
248 wchar_t *delim;
249 wchar_t wc1, wc2;
250 int n;
251 #endif /* PRESUNEUC */
252
253 cp = genbuf;
254 *cp = 0;
255 for (a1 = addr1; a1 <= addr2; a1++) {
256 getaline(*a1);
257 cp1 = linebuf;
258 if (a1 != addr1 && c == 0) {
259 while (*cp1 == ' ' || *cp1 == '\t')
260 cp1++;
261 if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
262 #ifndef PRESUNEUC
263 /*
264 * insert locale-specific word delimiter if
265 * either of end-of-former-line or
266 * top-of-latter-line is non-ASCII.
267 */
268 if (wddlm && *cp1 != ')' && cp[-1] != '.') {
269 if ((pcp = cp - MB_CUR_MAX) < genbuf)
270 pcp = genbuf;;
271 for ( ; pcp <= cp-1; pcp++) {
272 if ((n = mbtowc(&wc1,
273 (char *)pcp, cp - pcp)) ==
274 cp - pcp)
275 goto gotprev;
276 }
277 goto mberror;
278 gotprev:
279 if (!isascii(wc2 = *cp1)) {
280 if (mbtowc(&wc2, (char *) cp1,
281 MB_CUR_MAX) <= 0)
282 goto mberror;
283 }
284 delim = (*wddlm)(wc1,wc2,2);
285 while (*delim)
286 cp += wctomb((char *)cp,
287 *delim++);
288 *cp = 0;
289 } else
290 mberror:
291 #endif /* PRESUNEUC */
292 if (*cp1 != ')') {
293 *cp++ = ' ';
294 if (cp[-2] == '.')
295 *cp++ = ' ';
296 }
297 }
298 }
299 while (*cp++ = *cp1++)
300 if (cp > &genbuf[LBSIZE-2])
301 error(value(vi_TERSE) ? gettext("Line overflow") :
302 gettext("Result line of join would be too long"));
303 cp--;
304 }
305 strcLIN(genbuf);
306 (void) delete(0);
307 jcount = 1;
308 if (FIXUNDO)
309 undap1 = undap2 = addr1;
310 (void)append(jnoop, --addr1);
311 if (FIXUNDO)
312 vundkind = VMANY;
313 return (0);
314 }
315
316 static int
jnoop(void)317 jnoop(void)
318 {
319
320 return(--jcount);
321 }
322
323 /*
324 * Move and copy lines. Hard work is done by move1 which
325 * is also called by undo.
326 */
327 int getcopy();
328
329 void
vi_move(void)330 vi_move(void)
331 {
332 line *adt;
333 bool iscopy = 0;
334
335 if (Command[0] == 'm') {
336 setdot1();
337 markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
338 } else {
339 iscopy++;
340 setdot();
341 }
342 nonzero();
343 adt = address((char*)0);
344 if (adt == 0)
345 serror(value(vi_TERSE) ?
346 (unsigned char *)gettext("%s where?") :
347 (unsigned char *)gettext("%s requires a trailing address"),
348 Command);
349 donewline();
350 move1(iscopy, adt);
351 killed();
352 }
353
354 void
move1(int cflag,line * addrt)355 move1(int cflag, line *addrt)
356 {
357 line *adt, *ad1, *ad2;
358 int nlines;
359
360 adt = addrt;
361 nlines = (addr2 - addr1) + 1;
362 if (cflag) {
363 tad1 = addr1;
364 ad1 = dol;
365 (void)append(getcopy, ad1++);
366 ad2 = dol;
367 } else {
368 ad2 = addr2;
369 for (ad1 = addr1; ad1 <= ad2;)
370 *ad1++ &= ~01;
371 ad1 = addr1;
372 }
373 ad2++;
374 if (adt < ad1) {
375 if (adt + 1 == ad1 && !cflag && !inglobal)
376 error(gettext("That move would do nothing!"));
377 dot = adt + (ad2 - ad1);
378 if (++adt != ad1) {
379 reverse(adt, ad1);
380 reverse(ad1, ad2);
381 reverse(adt, ad2);
382 }
383 } else if (adt >= ad2) {
384 dot = adt++;
385 reverse(ad1, ad2);
386 reverse(ad2, adt);
387 reverse(ad1, adt);
388 } else
389 error(gettext("Move to a moved line"));
390 change();
391 if (!inglobal)
392 if(FIXUNDO) {
393 if (cflag) {
394 undap1 = addrt + 1;
395 undap2 = undap1 + nlines;
396 deletenone();
397 } else {
398 undkind = UNDMOVE;
399 undap1 = addr1;
400 undap2 = addr2;
401 unddel = addrt;
402 squish();
403 }
404 }
405 }
406
407 int
getcopy(void)408 getcopy(void)
409 {
410
411 if (tad1 > addr2)
412 return (EOF);
413 getaline(*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 getaline(*tad1++);
427 tad1++;
428 return (0);
429 }
430
431 int
put(void)432 put(void)
433 {
434 int cnt;
435
436 if (!FIXUNDO)
437 error(gettext("Cannot put inside global/macro"));
438 cnt = unddol - dol;
439 if (cnt && inopen && pkill[0] && pkill[1]) {
440 pragged(1);
441 return (0);
442 }
443 tad1 = dol + 1;
444 (void)append(getput, addr2);
445 undkind = UNDPUT;
446 notecnt = cnt;
447 netchange(cnt);
448 return (0);
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(bool kill)458 pragged(bool kill)
459 {
460 extern unsigned char *cursor;
461 #ifdef XPG4
462 extern int P_cursor_offset;
463 #endif
464 unsigned char *gp = &genbuf[cursor - linebuf];
465
466 /*
467 * Assume the editor has:
468 *
469 * cursor is on 'c'
470 *
471 * file is: 1) abcd
472 * 2) efgh
473 *
474 * undo area: 3) 1
475 * 4) 2
476 * 5) 3
477 */
478
479 if (!kill)
480 getDOT();
481
482 /*
483 * Copy "abcd" into genbuf.
484 * Note that gp points to 'c'.
485 */
486
487 strcpy(genbuf, linebuf);
488
489 /*
490 * Get last line of undo area ("3") into linebuf.
491 */
492
493 getaline(*unddol);
494 if (kill)
495 *pkill[1] = 0;
496
497
498 /*
499 * Concatenate trailing end of current line
500 * into the last line of undo area:
501 * linebuf = "3cd"
502 */
503
504 strcat(linebuf, gp);
505 #ifdef XPG4
506 P_cursor_offset = strlen(linebuf) - strlen(gp) - 1;
507 #endif
508
509 /*
510 * Replace the last line with what is now in linebuf.
511 * So unddol = "3cd"
512 */
513
514 putmark(unddol);
515
516 /*
517 * Get the first line of the undo save area into linebuf.
518 * So linebuf = "1"
519 */
520
521 getaline(dol[1]);
522 if (kill)
523 strcLIN(pkill[0]);
524
525 /*
526 * Copy the first line of the undo save area
527 * over what is pointed to by sp.
528 * genbuf = "ab1"
529 */
530
531 strcpy(gp, linebuf);
532
533 /*
534 * Now copy genbuf back into linebuf.
535 * linebuf = "ab1"
536 */
537
538 strcLIN(genbuf);
539
540 /*
541 * Now put linebuf back into the first line
542 * of the undo save area.
543 */
544
545 putmark(dol+1);
546
547 /*
548 * Prepare to perform an undo which will actually
549 * do a put of multiple lines in the middle of
550 * the current line.
551 */
552
553 undkind = UNDCHANGE;
554 undap1 = dot;
555 undap2 = dot + 1;
556 unddel = dot - 1;
557 undo(1);
558 }
559
560 /*
561 * Shift lines, based on c.
562 * If c is neither < nor >, then this is a lisp aligning =.
563 */
564 void
shift(int c,int cnt)565 shift(int c, int cnt)
566 {
567 line *addr;
568 unsigned char *cp;
569 unsigned char *dp;
570 int i;
571
572 if(FIXUNDO)
573 save12(), undkind = UNDCHANGE;
574 cnt *= value(vi_SHIFTWIDTH);
575 for (addr = addr1; addr <= addr2; addr++) {
576 dot = addr;
577 if (c == '=' && addr == addr1 && addr != addr2)
578 continue;
579 getDOT();
580 i = whitecnt(linebuf);
581 switch (c) {
582
583 case '>':
584 if (linebuf[0] == 0)
585 continue;
586 cp = genindent(i + cnt);
587 break;
588
589 case '<':
590 if (i == 0)
591 continue;
592 i -= cnt;
593 cp = i > 0 ? genindent(i) : genbuf;
594 break;
595
596 default:
597 i = lindent(addr);
598 getDOT();
599 cp = genindent(i);
600 break;
601 }
602 if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
603 error(value(vi_TERSE) ? gettext("Line too long") :
604 gettext("Result line after shift would be too long"));
605 CP(cp, dp);
606 strcLIN(genbuf);
607 putmark(addr);
608 }
609 killed();
610 }
611
612 /*
613 * Find a tag in the tags file.
614 * Most work here is in parsing the tags file itself.
615 */
616 void
tagfind(quick)617 tagfind(quick)
618 bool quick;
619 {
620 unsigned char cmdbuf[BUFSIZE];
621 unsigned char filebuf[FNSIZE];
622 unsigned char tagfbuf[BUFSIZE];
623 int c, d;
624 bool samef = 1;
625 int tfcount = 0;
626 int omagic, tl;
627 unsigned char *fn, *fne;
628 #ifdef STDIO /* was VMUNIX */
629 /*
630 * We have lots of room so we bring in stdio and do
631 * a binary search on the tags file.
632 */
633 FILE *iof;
634 unsigned char iofbuf[BUFSIZE];
635 off64_t mid; /* assumed byte offset */
636 off64_t top, bot; /* length of tag file */
637 struct stat64 sbuf;
638 #endif
639
640 omagic = value(vi_MAGIC);
641 tl = value(vi_TAGLENGTH);
642 if (!skipend()) {
643 unsigned char *lp = lasttag;
644
645 while (!iswhite(peekchar()) && !endcmd(peekchar()))
646 if (lp < &lasttag[sizeof lasttag - 2])
647 *lp++ = getchar();
648 else
649 ignchar();
650 *lp++ = 0;
651 if (!endcmd(peekchar()))
652 badtag:
653 error(value(vi_TERSE) ? gettext("Bad tag") :
654 gettext("Give one tag per line"));
655 } else if (lasttag[0] == 0)
656 error(gettext("No previous tag"));
657 c = getchar();
658 if (!endcmd(c))
659 goto badtag;
660 if (c == EOF)
661 ungetchar(c);
662 clrstats();
663
664 /*
665 * Loop once for each file in tags "path".
666 *
667 * System tags array limits to 4k (tags[ONMSZ]) long,
668 * therefore, tagfbuf should be able to hold all tags.
669 */
670
671 CP(tagfbuf, svalue(vi_TAGS));
672 fne = tagfbuf - 1;
673 while (fne) {
674 fn = ++fne;
675 while (*fne && *fne != ' ')
676 fne++;
677 if (*fne == 0)
678 fne = 0; /* done, quit after this time */
679 else
680 *fne = 0; /* null terminate filename */
681 #ifdef STDIO /* was VMUNIX */
682 iof = fopen((char *)fn, "r");
683 if (iof == NULL)
684 continue;
685 tfcount++;
686 setbuf(iof, (char *)iofbuf);
687 fstat64(fileno(iof), &sbuf);
688 top = sbuf.st_size;
689 if (top == 0L || iof == NULL)
690 top = -1L;
691 bot = 0L;
692 while (top >= bot) {
693 /* loop for each tags file entry */
694 unsigned char *cp = linebuf;
695 unsigned char *lp = lasttag;
696 unsigned char *oglobp;
697
698 mid = (top + bot) / 2;
699 fseeko64(iof, mid, 0);
700 if (mid > 0) /* to get first tag in file to work */
701 /* scan to next \n */
702 if(fgets((char *)linebuf, sizeof linebuf, iof)==NULL)
703 goto goleft;
704 /* get the line itself */
705 if(fgets((char *)linebuf, sizeof linebuf, iof)==NULL)
706 goto goleft;
707 linebuf[strlen(linebuf)-1] = 0; /* was '\n' */
708 while (*cp && *lp == *cp)
709 cp++, lp++;
710 /*
711 * This if decides whether there is a tag match.
712 * A positive taglength means that a
713 * match is found if the tag given matches at least
714 * taglength chars of the tag found.
715 * A taglength of greater than 511 means that a
716 * match is found even if the tag given is a proper
717 * prefix of the tag found. i.e. "ab" matches "abcd"
718 */
719 if ( *lp == 0 && (iswhite(*cp) || tl > 511 || tl > 0 && lp-lasttag >= tl) ) {
720 /*
721 * Found a match. Force selection to be
722 * the first possible.
723 */
724 if ( mid == bot && mid == top ) {
725 ; /* found first possible match */
726 }
727 else {
728 /* postpone final decision. */
729 top = mid;
730 continue;
731 }
732 }
733 else {
734 if ((int)*lp > (int)*cp)
735 bot = mid + 1;
736 else
737 goleft:
738 top = mid - 1;
739 continue;
740 }
741 /*
742 * We found the tag. Decode the line in the file.
743 */
744 fclose(iof);
745
746 /* Rest of tag if abbreviated */
747 while (!iswhite(*cp))
748 cp++;
749
750 /* name of file */
751 while (*cp && iswhite(*cp))
752 cp++;
753 if (!*cp)
754 badtags:
755 serror((unsigned char *)
756 gettext("%s: Bad tags file entry"),
757 lasttag);
758 lp = filebuf;
759 while (*cp && *cp != ' ' && *cp != '\t') {
760 if (lp < &filebuf[sizeof filebuf - 2])
761 *lp++ = *cp;
762 cp++;
763 }
764 *lp++ = 0;
765
766 if (*cp == 0)
767 goto badtags;
768 if (dol != zero) {
769 /*
770 * Save current position in 't for ^^ in visual.
771 */
772 names['t'-'a'] = *dot &~ 01;
773 if (inopen) {
774 extern unsigned char *ncols['z'-'a'+2];
775 extern unsigned char *cursor;
776
777 ncols['t'-'a'] = cursor;
778 }
779 }
780 #ifdef TAG_STACK
781 if (*savedfile) {
782 savetag((char *)savedfile);
783 }
784 #endif
785 strcpy(cmdbuf, cp);
786 if (strcmp(filebuf, savedfile) || !edited) {
787 unsigned char cmdbuf2[sizeof filebuf + 10];
788
789 /* Different file. Do autowrite & get it. */
790 if (!quick) {
791 ckaw();
792 if (chng && dol > zero) {
793 #ifdef TAG_STACK
794 unsavetag();
795 #endif
796 error(value(vi_TERSE) ?
797 gettext("No write") : gettext("No write since last change (:tag! overrides)"));
798 }
799 }
800 oglobp = globp;
801 strcpy(cmdbuf2, "e! ");
802 strcat(cmdbuf2, filebuf);
803 globp = cmdbuf2;
804 d = peekc; ungetchar(0);
805 commands(1, 1);
806 peekc = d;
807 globp = oglobp;
808 value(vi_MAGIC) = omagic;
809 samef = 0;
810 }
811
812 /*
813 * Look for pattern in the current file.
814 */
815 oglobp = globp;
816 globp = cmdbuf;
817 d = peekc; ungetchar(0);
818 if (samef)
819 markpr(dot);
820 /*
821 * BUG: if it isn't found (user edited header
822 * line) we get left in nomagic mode.
823 */
824 value(vi_MAGIC) = 0;
825 commands(1, 1);
826 peekc = d;
827 globp = oglobp;
828 value(vi_MAGIC) = omagic;
829 return;
830 } /* end of "for each tag in file" */
831 #endif /* STDIO */
832 /*
833 * Binary search failed, so try linear search if -S is on.
834 * -S is needed for tags files that are not sorted.
835 */
836
837 /*
838 * Avoid stdio and scan tag file linearly.
839 */
840 if (tags_flag == 0)
841 continue;
842 io = open(fn, 0);
843 if (io < 0)
844 continue;
845 /* tfcount++; */
846 while (getfile() == 0) {
847 /* loop for each tags file entry */
848 unsigned char *cp = linebuf;
849 unsigned char *lp = lasttag;
850 unsigned char *oglobp;
851
852 while (*cp && *lp == *cp)
853 cp++, lp++;
854 /*
855 * This if decides whether there is a tag match.
856 * A positive taglength means that a
857 * match is found if the tag given matches at least
858 * taglength chars of the tag found.
859 * A taglength of greater than 511 means that a
860 * match is found even if the tag given is a proper
861 * prefix of the tag found. i.e. "ab" matches "abcd"
862 */
863 if ( *lp == 0 && (iswhite(*cp) || tl > 511 || tl > 0 && lp-lasttag >= tl) ) {
864 ; /* Found it. */
865 }
866 else {
867 /* Not this tag. Try the next */
868 continue;
869 }
870 /*
871 * We found the tag. Decode the line in the file.
872 */
873 close(io);
874 /* Rest of tag if abbreviated */
875 while (!iswhite(*cp))
876 cp++;
877
878 /* name of file */
879 while (*cp && iswhite(*cp))
880 cp++;
881 if (!*cp)
882 badtags2:
883 serror((unsigned char *)
884 gettext("%s: Bad tags file entry"),
885 lasttag);
886 lp = filebuf;
887 while (*cp && *cp != ' ' && *cp != '\t') {
888 if (lp < &filebuf[sizeof filebuf - 2])
889 *lp++ = *cp;
890 cp++;
891 }
892 *lp++ = 0;
893
894 if (*cp == 0)
895 goto badtags2;
896 if (dol != zero) {
897 /*
898 * Save current position in 't for ^^ in visual.
899 */
900 names['t'-'a'] = *dot &~ 01;
901 if (inopen) {
902 extern unsigned char *ncols['z'-'a'+2];
903 extern unsigned char *cursor;
904
905 ncols['t'-'a'] = cursor;
906 }
907 }
908 #ifdef TAG_STACK
909 if (*savedfile) {
910 savetag((char *)savedfile);
911 }
912 #endif
913 strcpy(cmdbuf, cp);
914 if (strcmp(filebuf, savedfile) || !edited) {
915 unsigned char cmdbuf2[sizeof filebuf + 10];
916
917 /* Different file. Do autowrite & get it. */
918 if (!quick) {
919 ckaw();
920 if (chng && dol > zero) {
921 #ifdef TAG_STACK
922 unsavetag();
923 #endif
924 error(value(vi_TERSE) ?
925 gettext("No write") : gettext("No write since last change (:tag! overrides)"));
926 }
927 }
928 oglobp = globp;
929 strcpy(cmdbuf2, "e! ");
930 strcat(cmdbuf2, filebuf);
931 globp = cmdbuf2;
932 d = peekc; ungetchar(0);
933 commands(1, 1);
934 peekc = d;
935 globp = oglobp;
936 value(vi_MAGIC) = omagic;
937 samef = 0;
938 }
939
940 /*
941 * Look for pattern in the current file.
942 */
943 oglobp = globp;
944 globp = cmdbuf;
945 d = peekc; ungetchar(0);
946 if (samef)
947 markpr(dot);
948 /*
949 * BUG: if it isn't found (user edited header
950 * line) we get left in nomagic mode.
951 */
952 value(vi_MAGIC) = 0;
953 commands(1, 1);
954 peekc = d;
955 globp = oglobp;
956 value(vi_MAGIC) = omagic;
957 return;
958 } /* end of "for each tag in file" */
959
960 /*
961 * No such tag in this file. Close it and try the next.
962 */
963 #ifdef STDIO /* was VMUNIX */
964 fclose(iof);
965 #else
966 close(io);
967 #endif
968 } /* end of "for each file in path" */
969 if (tfcount <= 0)
970 error(gettext("No tags file"));
971 else
972 serror(value(vi_TERSE) ?
973 (unsigned char *)gettext("%s: No such tag") :
974 (unsigned char *)gettext("%s: No such tag in tags file"),
975 lasttag);
976 }
977
978 /*
979 * Save lines from addr1 thru addr2 as though
980 * they had been deleted.
981 */
982 int
yank(void)983 yank(void)
984 {
985
986 if (!FIXUNDO)
987 error(gettext("Can't yank inside global/macro"));
988 save12();
989 undkind = UNDNONE;
990 killcnt(addr2 - addr1 + 1);
991 return (0);
992 }
993
994 /*
995 * z command; print windows of text in the file.
996 *
997 * If this seems unreasonably arcane, the reasons
998 * are historical. This is one of the first commands
999 * added to the first ex (then called en) and the
1000 * number of facilities here were the major advantage
1001 * of en over ed since they allowed more use to be
1002 * made of fast terminals w/o typing .,.22p all the time.
1003 */
1004 bool zhadpr;
1005 bool znoclear;
1006 short zweight;
1007
1008 void
zop(int hadpr)1009 zop(int hadpr)
1010 {
1011 int c, nlines, op;
1012 bool excl;
1013
1014 zhadpr = hadpr;
1015 notempty();
1016 znoclear = 0;
1017 zweight = 0;
1018 excl = exclam();
1019 switch (c = op = getchar()) {
1020
1021 case '^':
1022 zweight = 1;
1023 /* FALLTHROUGH */
1024 case '-':
1025 case '+':
1026 while (peekchar() == op) {
1027 ignchar();
1028 zweight++;
1029 }
1030 /* FALLTHROUGH */
1031 case '=':
1032 case '.':
1033 c = getchar();
1034 break;
1035
1036 case EOF:
1037 znoclear++;
1038 break;
1039
1040 default:
1041 op = 0;
1042 break;
1043 }
1044 if (isdigit(c)) {
1045 nlines = c - '0';
1046 for(;;) {
1047 c = getchar();
1048 if (!isdigit(c))
1049 break;
1050 nlines *= 10;
1051 nlines += c - '0';
1052 }
1053 if (nlines < lines)
1054 znoclear++;
1055 value(vi_WINDOW) = nlines;
1056 if (op == '=')
1057 nlines += 2;
1058 }
1059 else {
1060 nlines = op == EOF ? value(vi_SCROLL) :
1061 excl ? lines - 1 : value(vi_WINDOW);
1062 }
1063 if (inopen || c != EOF) {
1064 ungetchar(c);
1065 donewline();
1066 }
1067 addr1 = addr2;
1068 if (addr2 == 0 && dot < dol && op == 0)
1069 addr1 = addr2 = dot+1;
1070 setdot();
1071 zop2(nlines, op);
1072 }
1073
1074 void
zop2(int nlines,int op)1075 zop2(int nlines, int op)
1076 {
1077 line *split;
1078
1079 split = NULL;
1080 switch (op) {
1081
1082 case EOF:
1083 if (addr2 == dol)
1084 error(gettext("\nAt EOF"));
1085 /* FALLTHROUGH */
1086 case '+':
1087 if (addr2 == dol)
1088 error(gettext("At EOF"));
1089 addr2 += nlines * zweight;
1090 if (addr2 > dol)
1091 error(gettext("Hit BOTTOM"));
1092 addr2++;
1093 /* FALLTHROUGH */
1094 default:
1095 addr1 = addr2;
1096 addr2 += nlines-1;
1097 dot = addr2;
1098 break;
1099
1100 case '=':
1101 case '.':
1102 znoclear = 0;
1103 nlines--;
1104 nlines >>= 1;
1105 if (op == '=')
1106 nlines--;
1107 addr1 = addr2 - nlines;
1108 if (op == '=')
1109 dot = split = addr2;
1110 addr2 += nlines;
1111 if (op == '.') {
1112 markDOT();
1113 dot = addr2;
1114 }
1115 break;
1116
1117 case '^':
1118 case '-':
1119 addr2 -= nlines * zweight;
1120 if (addr2 < one)
1121 error(gettext("Hit TOP"));
1122 nlines--;
1123 addr1 = addr2 - nlines;
1124 dot = addr2;
1125 break;
1126 }
1127 if (addr1 <= zero)
1128 addr1 = one;
1129 if (addr2 > dol)
1130 addr2 = dol;
1131 if (dot > dol)
1132 dot = dol;
1133 if (addr1 > addr2)
1134 return;
1135 if (op == EOF && zhadpr) {
1136 getaline(*addr1);
1137 putchar((int)('\r' | QUOTE));
1138 shudclob = 1;
1139 } else if (znoclear == 0 && clear_screen != NOSTR && !inopen) {
1140 flush1();
1141 vclear();
1142 }
1143 if (addr2 - addr1 > 1)
1144 pstart();
1145 if (split) {
1146 plines(addr1, split - 1, 0);
1147 splitit();
1148 plines(split, split, 0);
1149 splitit();
1150 addr1 = split + 1;
1151 }
1152 plines(addr1, addr2, 0);
1153 }
1154
1155 static void
splitit(void)1156 splitit(void)
1157 {
1158 int l;
1159
1160 for (l = columns > 80 ? 40 : columns / 2; l > 0; l--)
1161 putchar('-');
1162 putnl();
1163 }
1164
1165 void
plines(line * adr1,line * adr2,bool movedot)1166 plines(line *adr1, line *adr2, bool movedot)
1167 {
1168 line *addr;
1169
1170 pofix();
1171 for (addr = adr1; addr <= adr2; addr++) {
1172 getaline(*addr);
1173 pline(lineno(addr));
1174 if (inopen)
1175 putchar((int)('\n' | QUOTE));
1176 if (movedot)
1177 dot = addr;
1178 }
1179 }
1180
1181 void
pofix(void)1182 pofix(void)
1183 {
1184
1185 if (inopen && Outchar != termchar) {
1186 vnfl();
1187 setoutt();
1188 }
1189 }
1190
1191 /*
1192 * Command level undo works easily because
1193 * the editor has a unique temporary file
1194 * index for every line which ever existed.
1195 * We don't have to save large blocks of text,
1196 * only the indices which are small. We do this
1197 * by moving them to after the last line in the
1198 * line buffer array, and marking down info
1199 * about whence they came.
1200 *
1201 * Undo is its own inverse.
1202 */
1203 void
undo(bool c)1204 undo(bool c)
1205 {
1206 int i, k;
1207 line *jp, *kp, *j;
1208 line *dolp1, *newdol, *newadot;
1209
1210 #ifdef UNDOTRACE
1211 if (trace)
1212 vudump("before undo");
1213 #endif
1214 if (inglobal && inopen <= 0)
1215 error(value(vi_TERSE) ? gettext("Can't undo in global") :
1216 gettext("Can't undo in global commands"));
1217
1218 /*
1219 * Unless flag indicates a forced undo, make sure
1220 * there really was a change before trying to undo it.
1221 */
1222
1223 if (!c)
1224 somechange();
1225
1226 /*
1227 * Update change flags.
1228 */
1229
1230 pkill[0] = pkill[1] = 0;
1231 change();
1232 if (undkind == UNDMOVE) {
1233 /*
1234 * Command to be undone is a move command.
1235 * This is handled as a special case by noting that
1236 * a move "a,b m c" can be inverted by another move.
1237 */
1238 if ((i = (jp = unddel) - undap2) > 0) {
1239 /*
1240 * when c > b inverse is a+(c-b),c m a-1
1241 */
1242 addr2 = jp;
1243 addr1 = (jp = undap1) + i;
1244 unddel = jp-1;
1245 } else {
1246 /*
1247 * when b > c inverse is c+1,c+1+(b-a) m b
1248 */
1249 addr1 = ++jp;
1250 addr2 = jp + ((unddel = undap2) - undap1);
1251 }
1252 kp = undap1;
1253 move1(0, unddel);
1254 dot = kp;
1255 Command = (unsigned char *)"move";
1256 killed();
1257 } else {
1258 int cnt;
1259
1260 newadot = dot;
1261 cnt = lineDOL();
1262 newdol = dol;
1263 dolp1 = dol + 1;
1264 /*
1265 * Command to be undone is a non-move.
1266 * All such commands are treated as a combination of
1267 * a delete command and a append command.
1268 * We first move the lines appended by the last command
1269 * from undap1 to undap2-1 so that they are just before the
1270 * saved deleted lines.
1271 *
1272 * Assume the editor has:
1273 *
1274 * cursor is on 'c'
1275 *
1276 * (just change lines 5-8)
1277 *
1278 * file is: 1) ab
1279 * 2) cd
1280 * 3) ef
1281 * 4) gh
1282 * undap1: 5) 12
1283 * 6) 34
1284 * 7) 56
1285 * 8) 78
1286 * undap2: 9) qr
1287 * 10) st
1288 * 11) uv
1289 * 12) wx
1290 * dol: 13) yz
1291 *
1292 * UNDO AREA:
1293 * dol+1: 5) ij
1294 * 6) kl
1295 * 7) mn
1296 * unddol: 8) op
1297 */
1298
1299 /*
1300 * If this is a change (not a delete/put),
1301 * then we must move the text between undap1 and undap2
1302 * and it must not be at the bottom of the file
1303 */
1304
1305 if ((i = (kp = undap2) - (jp = undap1)) > 0) {
1306 if (kp != dolp1) {
1307
1308 /*
1309 * FILE: LINE INITIAL REV1 REV2 REV3
1310 *
1311 * 1) ab ab ab ab
1312 * 2) cd cd cd cd
1313 * 3) ef ef ef ef
1314 * unddel: 4) gh gh gh gh
1315 * undap1: 5) 12 78 78 qr
1316 * 6) 34 56 56 st
1317 * 7) 56 34 34 uv
1318 * 8) 78 12 12 wx
1319 * undap2: 9) qr qr yz yz
1320 * 10) st st wx 12
1321 * 11) uv uv uv 34
1322 * 12) wx wx st 56
1323 * dol: 13) yz yz qr 78
1324 *
1325 * UNDO AREA:
1326 * dol+1: 5) ij ij ij ij
1327 * 6) kl kl kl kl
1328 * 7) mn mn mn mn
1329 * unddol: 8) op op op op
1330 */
1331
1332 reverse(jp, kp);
1333 reverse(kp, dolp1);
1334 reverse(jp, dolp1);
1335 }
1336 /*
1337 * Unddel, the line just before the spot where this
1338 * test was deleted, may have moved. Account for
1339 * this in restoration of saved deleted lines.
1340 */
1341 if (unddel >= jp)
1342 unddel -= i;
1343
1344 /*
1345 * The last line (dol) may have changed,
1346 * account for this.
1347 */
1348 newdol -= i;
1349
1350 /*
1351 * For the case where no lines are restored, dot
1352 * is the line before the first line deleted.
1353 */
1354 dot = jp-1;
1355 }
1356 /*
1357 * Now put the deleted lines, if any, back where they were.
1358 * Basic operation is: dol+1,unddol m unddel
1359 */
1360 if (undkind == UNDPUT) {
1361 unddel = undap1 - 1;
1362 squish();
1363 }
1364
1365 /*
1366 * Set jp to the line where deleted text is to be added.
1367 */
1368 jp = unddel + 1;
1369
1370 /*
1371 * Set kp to end of undo save area.
1372 *
1373 * If there is any deleted text to be added, do reverses.
1374 */
1375
1376 if ((i = (kp = unddol) - dol) > 0) {
1377
1378 /*
1379 * If deleted lines are not to be appended
1380 * to the bottom of the file...
1381 */
1382
1383 if (jp != dolp1) {
1384 /*
1385 * FILE: LINE START REV1 REV2 REV3
1386 * 1) ab ab ab ab
1387 * 2) cd cd cd cd
1388 * 3) ef ef ef ef
1389 * unddel: 4) gh gh gh gh
1390 * undap1: 5) qr 78 78 ij
1391 * 6) st 56 56 kl
1392 * 7) uv 34 34 mn
1393 * 8) wx 12 12 op
1394 * undap2: 9) yz yz yz qr
1395 * 10) 12 wx wx st
1396 * 11) 34 uv uv uv
1397 * 12) 56 st st wx
1398 * dol: 13) 78 qr qr yz
1399 *
1400 * UNDO AREA:
1401 * dol+1: 5) ij ij op 12
1402 * 6) kl kl mn 34
1403 * 7) mn mn kl 56
1404 * unddol: 8) op op ij 78
1405 */
1406
1407 reverse(jp, dolp1);
1408 reverse(dolp1, ++kp);
1409 reverse(jp, kp);
1410 }
1411 /*
1412 * Account for possible forward motion of the target
1413 * (where the deleted lines were restored) for after
1414 * restoration of the deleted lines.
1415 */
1416 if (undap1 >= jp)
1417 undap1 += i;
1418 /*
1419 * Dot is the first resurrected line.
1420 */
1421 dot = jp;
1422
1423 /*
1424 * Account for a shift in the last line (dol).
1425 */
1426
1427 newdol += i;
1428 }
1429 /*
1430 * Clean up so we are invertible
1431 */
1432 unddel = undap1 - 1;
1433 undap1 = jp;
1434 undap2 = jp + i;
1435 dol = newdol;
1436 netchHAD(cnt);
1437 if (undkind == UNDALL) {
1438 dot = undadot;
1439 undadot = newadot;
1440 } else
1441 undkind = UNDCHANGE;
1442 /*
1443 * Now relocate all marks for lines that were modified,
1444 * since the marks point to lines whose address has
1445 * been modified from the save area to the current
1446 * area
1447 */
1448
1449 for (j=unddol; j> dol; j--)
1450 for (k=0; k<=25; k++)
1451 if (names[k] == *(j))
1452 names[k]= *((undap1+(j-dolp1)) );
1453 }
1454 /*
1455 * Defensive programming - after a munged undadot.
1456 * Also handle empty buffer case.
1457 */
1458 if ((dot <= zero || dot > dol) && dot != dol)
1459 dot = one;
1460 #ifdef UNDOTRACE
1461 if (trace)
1462 vudump("after undo");
1463 #endif
1464 }
1465
1466 /*
1467 * Be (almost completely) sure there really
1468 * was a change, before claiming to undo.
1469 */
1470 void
somechange(void)1471 somechange(void)
1472 {
1473 line *ip, *jp;
1474
1475 switch (undkind) {
1476
1477 case UNDMOVE:
1478 return;
1479
1480 case UNDCHANGE:
1481 if (undap1 == undap2 && dol == unddol)
1482 break;
1483 return;
1484
1485 case UNDPUT:
1486 if (undap1 != undap2)
1487 return;
1488 break;
1489
1490 case UNDALL:
1491 if (unddol - dol != lineDOL())
1492 return;
1493 for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
1494 if ((*ip &~ 01) != (*jp &~ 01))
1495 return;
1496 break;
1497
1498 case UNDNONE:
1499 error(gettext("Nothing to undo"));
1500 }
1501 error(value(vi_TERSE) ? gettext("Nothing changed") :
1502 gettext("Last undoable command didn't change anything"));
1503 }
1504
1505 /*
1506 * Map command:
1507 * map src dest
1508 *
1509 * un is true if this is unmap command
1510 * ab is true if this is abbr command
1511 */
1512 void
mapcmd(int un,int ab)1513 mapcmd(int un, int ab)
1514 {
1515 unsigned char lhs[100], rhs[100]; /* max sizes resp. */
1516 unsigned char *p;
1517 int c; /* char --> int */
1518 unsigned char *dname;
1519 struct maps *mp; /* the map structure we are working on */
1520 unsigned char funkey[3];
1521
1522 mp = ab ? abbrevs : exclam() ? immacs : arrows;
1523 if (skipend()) {
1524 int i;
1525
1526 /* print current mapping values */
1527 if (peekchar() != EOF)
1528 ignchar();
1529 if (un)
1530 error(gettext("Missing lhs"));
1531 if (inopen)
1532 pofix();
1533 for (i=0; i< MAXNOMACS && mp[i].mapto; i++)
1534 if (mp[i].cap) {
1535 lprintf("%s", mp[i].descr);
1536 putchar('\t');
1537 lprintf("%s", mp[i].cap);
1538 putchar('\t');
1539 lprintf("%s", mp[i].mapto);
1540 putNFL();
1541 }
1542 return;
1543 }
1544
1545 (void)skipwh();
1546 for (p=lhs; ; ) {
1547 c = getchar();
1548 if (c == CTRL('v')) {
1549 c = getchar();
1550 } else if (!un && any(c, " \t")) {
1551 /* End of lhs */
1552 break;
1553 } else if (endcmd(c) && c!='"') {
1554 ungetchar(c);
1555 if (un) {
1556 donewline();
1557 *p = 0;
1558 addmac(lhs, (unsigned char *)NOSTR,
1559 (unsigned char *)NOSTR, mp);
1560 return;
1561 } else
1562 error(gettext("Missing rhs"));
1563 }
1564 *p++ = c;
1565 }
1566 *p = 0;
1567
1568 if (skipend())
1569 error(gettext("Missing rhs"));
1570 for (p=rhs; ; ) {
1571 c = getchar();
1572 if (c == CTRL('v')) {
1573 c = getchar();
1574 } else if (endcmd(c) && c!='"') {
1575 ungetchar(c);
1576 break;
1577 }
1578 *p++ = c;
1579 }
1580 *p = 0;
1581 donewline();
1582 /*
1583 * Special hack for function keys: #1 means key f1, etc.
1584 * If the terminal doesn't have function keys, we just use #1.
1585 */
1586 if (lhs[0] == '#') {
1587 unsigned char *fnkey;
1588 unsigned char *fkey();
1589
1590 fnkey = fkey(lhs[1] - '0');
1591 funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
1592 if (fnkey)
1593 strcpy(lhs, fnkey);
1594 dname = funkey;
1595 } else {
1596 dname = lhs;
1597 }
1598 addmac(lhs,rhs,dname,mp);
1599 }
1600
1601 /*
1602 * Add a macro definition to those that already exist. The sequence of
1603 * chars "src" is mapped into "dest". If src is already mapped into something
1604 * this overrides the mapping. There is no recursion. Unmap is done by
1605 * using NOSTR for dest. Dname is what to show in listings. mp is
1606 * the structure to affect (arrows, etc).
1607 */
1608 void
addmac(unsigned char * src,unsigned char * dest,unsigned char * dname,struct maps * mp)1609 addmac(unsigned char *src, unsigned char *dest, unsigned char *dname,
1610 struct maps *mp)
1611 {
1612 int slot, zer;
1613
1614 #ifdef UNDOTRACE
1615 if (trace)
1616 fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
1617 #endif
1618 if (dest && mp==arrows) {
1619 /*
1620 * Prevent tail recursion. We really should be
1621 * checking to see if src is a suffix of dest
1622 * but this makes mapping involving escapes that
1623 * is reasonable mess up.
1624 */
1625 if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
1626 error(gettext("No tail recursion"));
1627 /*
1628 * We don't let the user rob themself of ":", and making
1629 * multi char words is a bad idea so we don't allow it.
1630 * Note that if user sets mapinput and maps all of return,
1631 * linefeed, and escape, they can hurt themself. This is
1632 * so weird I don't bother to check for it.
1633 */
1634 if (isalpha(src[0]) && isascii(src[0]) && src[1] || any(src[0],":"))
1635 error(gettext("Too dangerous to map that"));
1636 }
1637 else if (dest) {
1638 /* check for tail recursion in input mode: fussier */
1639 if (eq(src, dest+strlen(dest)-strlen(src)))
1640 error(gettext("No tail recursion"));
1641 }
1642 /*
1643 * If the src were null it would cause the dest to
1644 * be mapped always forever. This is not good.
1645 */
1646 if (src == (unsigned char *)NOSTR || src[0] == 0)
1647 error(gettext("Missing lhs"));
1648
1649 /* see if we already have a def for src */
1650 zer = -1;
1651 for (slot=0; slot < MAXNOMACS && mp[slot].mapto; slot++) {
1652 if (mp[slot].cap) {
1653 if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
1654 break; /* if so, reuse slot */
1655 } else {
1656 zer = slot; /* remember an empty slot */
1657 }
1658 }
1659
1660 if (slot >= MAXNOMACS)
1661 error(gettext("Too many macros"));
1662
1663 if (dest == (unsigned char *)NOSTR) {
1664 /* unmap */
1665 if (mp[slot].cap) {
1666 mp[slot].cap = (unsigned char *)NOSTR;
1667 mp[slot].descr = (unsigned char *)NOSTR;
1668 } else {
1669 error(value(vi_TERSE) ? gettext("Not mapped") :
1670 gettext("That macro wasn't mapped"));
1671 }
1672 return;
1673 }
1674
1675 /* reuse empty slot, if we found one and src isn't already defined */
1676 if (zer >= 0 && mp[slot].mapto == 0)
1677 slot = zer;
1678
1679 /* if not, append to end */
1680 if (msnext == 0) /* first time */
1681 msnext = mapspace;
1682 /* Check is a bit conservative, we charge for dname even if reusing src */
1683 if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS)
1684 error(gettext("Too much macro text"));
1685 CP(msnext, src);
1686 mp[slot].cap = msnext;
1687 msnext += strlen(src) + 1; /* plus 1 for null on the end */
1688 CP(msnext, dest);
1689 mp[slot].mapto = msnext;
1690 msnext += strlen(dest) + 1;
1691 if (dname) {
1692 CP(msnext, dname);
1693 mp[slot].descr = msnext;
1694 msnext += strlen(dname) + 1;
1695 } else {
1696 /* default descr to string user enters */
1697 mp[slot].descr = src;
1698 }
1699 }
1700
1701 /*
1702 * Implements macros from command mode. c is the buffer to
1703 * get the macro from.
1704 */
1705 void
cmdmac(c)1706 cmdmac(c)
1707 unsigned char c;
1708 {
1709 unsigned char macbuf[BUFSIZE];
1710 line *ad, *a1, *a2;
1711 unsigned char *oglobp;
1712 short pk;
1713 bool oinglobal;
1714
1715 lastmac = c;
1716 oglobp = globp;
1717 oinglobal = inglobal;
1718 pk = peekc; peekc = 0;
1719 if (inglobal < 2)
1720 inglobal = 1;
1721 regbuf(c, macbuf, sizeof(macbuf));
1722 a1 = addr1; a2 = addr2;
1723 for (ad=a1; ad<=a2; ad++) {
1724 globp = macbuf;
1725 dot = ad;
1726 commands(1,1);
1727 }
1728 globp = oglobp;
1729 inglobal = oinglobal;
1730 peekc = pk;
1731 }
1732
1733 unsigned char *
vgetpass(prompt)1734 vgetpass(prompt)
1735 char *prompt;
1736 {
1737 unsigned char *p;
1738 int c;
1739 static unsigned char pbuf[9];
1740
1741 /* In ex mode, let the system hassle with setting no echo */
1742 if (!inopen)
1743 return (unsigned char *)getpass(prompt);
1744 viprintf("%s", prompt); flush();
1745 for (p=pbuf; (c = getkey())!='\n' && c!=EOF && c!='\r';) {
1746 if (p < &pbuf[8])
1747 *p++ = c;
1748 }
1749 *p = '\0';
1750 return(pbuf);
1751 }
1752
1753
1754 #ifdef TAG_STACK
1755 #define TSTACKSIZE 20
1756 struct tagstack {
1757 line *tag_line;
1758 char *tag_file;
1759 } tagstack[TSTACKSIZE];
1760 static int tag_depth = 0;
1761
1762 static char tag_buf[ 1024 ];
1763 static char *tag_end = tag_buf;
1764
1765 void
savetag(char * name)1766 savetag(char *name) /* saves location where we are */
1767 {
1768 if( !value(vi_TAGSTACK) )
1769 return;
1770 if(tag_depth >= TSTACKSIZE) {
1771 error(gettext("Tagstack too deep."));
1772 }
1773 if( strlen( name ) + 1 + tag_end >= &tag_buf[1024]) {
1774 error(gettext("Too many tags."));
1775 }
1776 tagstack[tag_depth].tag_line = dot;
1777 tagstack[tag_depth++].tag_file = tag_end;
1778 while(*tag_end++ = *name++)
1779 ;
1780 }
1781
1782 /*
1783 * Undo a "savetag".
1784 */
1785 void
unsavetag(void)1786 unsavetag(void)
1787 {
1788 if (!value(vi_TAGSTACK))
1789 return;
1790 if (tag_depth > 0)
1791 tag_end = tagstack[--tag_depth].tag_file;
1792 }
1793
1794 void
poptag(quick)1795 poptag(quick) /* puts us back where we came from */
1796 bool quick;
1797 {
1798 unsigned char cmdbuf[100];
1799 unsigned char *oglobp;
1800 int d;
1801
1802 if (!value(vi_TAGSTACK)) { /* reset the stack */
1803 tag_end = tag_buf;
1804 d = tag_depth;
1805 tag_depth = 0;
1806 if (d == 0)
1807 error(gettext("Tagstack not enabled."));
1808 else
1809 return;
1810 }
1811 if (!tag_depth)
1812 error(gettext("Tagstack empty."));
1813
1814 /* change to old file */
1815 if (strcmp(tagstack[tag_depth-1].tag_file, savedfile) ) {
1816 if (!quick) {
1817 ckaw();
1818 if (chng && dol > zero)
1819 error(value(vi_TERSE) ?
1820 gettext("No write") : gettext("No write since last change (:pop! overrides)"));
1821 }
1822 oglobp = globp;
1823 strcpy(cmdbuf, "e! ");
1824 strcat(cmdbuf, tagstack[tag_depth-1].tag_file);
1825 globp = cmdbuf;
1826 d = peekc; ungetchar(0);
1827 commands(1, 1);
1828 peekc = d;
1829 globp = oglobp;
1830 }
1831 markpr(dot);
1832 /* set line number */
1833 dot = tagstack[--tag_depth].tag_line;
1834 tag_end = tagstack[tag_depth].tag_file;
1835 }
1836 #endif
1837