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