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