8655 remove more gender specific language
[unleashed.git] / usr / src / cmd / vi / port / ex_cmdsub.c
blob73cd8d75fe56da16e09a187e5afcebefb8192356
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 /* Copyright (c) 1981 Regents of the University of California */
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
46 * Command mode subroutines implementing
47 * append, args, copy, delete, join, move, put,
48 * shift, tag, yank, z and undo
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;
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.
63 int
64 append(int (*f)(), line *a)
66 line *a1, *a2, *rdot;
67 int nline;
69 nline = 0;
70 dot = a;
71 if(FIXUNDO && !inopen && f!=getsub) {
72 undap1 = undap2 = dot + 1;
73 undkind = UNDCHANGE;
75 while ((*f)() == 0) {
76 if (truedol >= endcore) {
77 if (morelines() < 0) {
78 if (FIXUNDO && f == getsub) {
79 undap1 = addr1;
80 undap2 = addr2 + 1;
82 error(value(vi_TERSE) ? gettext("Out of memory") :
83 gettext("Out of memory- too many lines in file"));
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();
103 return (nline);
106 void
107 appendnone(void)
110 if(FIXUNDO) {
111 undkind = UNDCHANGE;
112 undap1 = undap2 = addr1;
117 * Print out the argument list, with []'s around the current name.
119 void
120 pargs(void)
122 unsigned char **av = argv0, *as = args0;
123 int ac;
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;
135 noonl();
139 * Delete lines; two cases are if we are really deleting,
140 * more commonly we are just moving lines to the undo save area.
143 delete(bool hush)
145 line *a1, *a2;
147 nonzero();
148 if(FIXUNDO) {
149 void (*dsavint)();
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);
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;
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;
191 *a1++ = *a2++;
192 while (a2 <= a3);
193 a1 = addr1;
194 if (a1 > dol)
195 a1 = dol;
196 dot = a1;
198 if (!hush)
199 killed();
200 return (0);
203 void
204 deletenone(void)
207 if(FIXUNDO) {
208 undkind = UNDCHANGE;
209 squish();
210 unddel = addr1;
215 * Crush out the undo save area, moving the open/visual
216 * save area down in its place.
218 void
219 squish(void)
221 line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
223 if(FIXUNDO) {
224 if (inopen == -1)
225 return;
226 if (a1 < a2 && a2 < a3)
228 *a1++ = *a2++;
229 while (a2 < a3);
230 truedol -= unddol - dol;
231 unddol = dol;
236 * Join lines. Special hacks put in spaces, two spaces if
237 * preceding line ends with '.', or no spaces if next line starts with ).
239 static int jcount;
242 join(int c)
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 */
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
264 * insert locale-specific word delimiter if
265 * either of end-of-former-line or
266 * top-of-latter-line is non-ASCII.
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;
277 goto mberror;
278 gotprev:
279 if (!isascii(wc2 = *cp1)) {
280 if (mbtowc(&wc2, (char *) cp1,
281 MB_CUR_MAX) <= 0)
282 goto mberror;
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++ = ' ';
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--;
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);
316 static int
317 jnoop(void)
320 return(--jcount);
324 * Move and copy lines. Hard work is done by move1 which
325 * is also called by undo.
327 int getcopy();
329 void
330 vi_move(void)
332 line *adt;
333 bool iscopy = 0;
335 if (Command[0] == 'm') {
336 setdot1();
337 markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
338 } else {
339 iscopy++;
340 setdot();
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();
354 void
355 move1(int cflag, line *addrt)
357 line *adt, *ad1, *ad2;
358 int nlines;
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;
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);
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();
408 getcopy(void)
411 if (tad1 > addr2)
412 return (EOF);
413 getaline(*tad1++);
414 return (0);
418 * Put lines in the buffer from the undo save area.
421 getput(void)
424 if (tad1 > unddol)
425 return (EOF);
426 getaline(*tad1++);
427 tad1++;
428 return (0);
432 put(void)
434 int cnt;
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);
443 tad1 = dol + 1;
444 (void)append(getput, addr2);
445 undkind = UNDPUT;
446 notecnt = cnt;
447 netchange(cnt);
448 return (0);
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.
457 void
458 pragged(bool kill)
460 extern unsigned char *cursor;
461 #ifdef XPG4
462 extern int P_cursor_offset;
463 #endif
464 unsigned char *gp = &genbuf[cursor - linebuf];
467 * Assume the editor has:
469 * cursor is on 'c'
471 * file is: 1) abcd
472 * 2) efgh
474 * undo area: 3) 1
475 * 4) 2
476 * 5) 3
479 if (!kill)
480 getDOT();
483 * Copy "abcd" into genbuf.
484 * Note that gp points to 'c'.
487 strcpy(genbuf, linebuf);
490 * Get last line of undo area ("3") into linebuf.
493 getaline(*unddol);
494 if (kill)
495 *pkill[1] = 0;
499 * Concatenate trailing end of current line
500 * into the last line of undo area:
501 * linebuf = "3cd"
504 strcat(linebuf, gp);
505 #ifdef XPG4
506 P_cursor_offset = strlen(linebuf) - strlen(gp) - 1;
507 #endif
510 * Replace the last line with what is now in linebuf.
511 * So unddol = "3cd"
514 putmark(unddol);
517 * Get the first line of the undo save area into linebuf.
518 * So linebuf = "1"
521 getaline(dol[1]);
522 if (kill)
523 strcLIN(pkill[0]);
526 * Copy the first line of the undo save area
527 * over what is pointed to by sp.
528 * genbuf = "ab1"
531 strcpy(gp, linebuf);
534 * Now copy genbuf back into linebuf.
535 * linebuf = "ab1"
538 strcLIN(genbuf);
541 * Now put linebuf back into the first line
542 * of the undo save area.
545 putmark(dol+1);
548 * Prepare to perform an undo which will actually
549 * do a put of multiple lines in the middle of
550 * the current line.
553 undkind = UNDCHANGE;
554 undap1 = dot;
555 undap2 = dot + 1;
556 unddel = dot - 1;
557 undo(1);
561 * Shift lines, based on c.
562 * If c is neither < nor >, then this is a lisp aligning =.
564 void
565 shift(int c, int cnt)
567 line *addr;
568 unsigned char *cp;
569 unsigned char *dp;
570 int i;
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) {
583 case '>':
584 if (linebuf[0] == 0)
585 continue;
586 cp = genindent(i + cnt);
587 break;
589 case '<':
590 if (i == 0)
591 continue;
592 i -= cnt;
593 cp = i > 0 ? genindent(i) : genbuf;
594 break;
596 default:
597 i = lindent(addr);
598 getDOT();
599 cp = genindent(i);
600 break;
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);
609 killed();
613 * Find a tag in the tags file.
614 * Most work here is in parsing the tags file itself.
616 void
617 tagfind(quick)
618 bool quick;
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 */
630 * We have lots of room so we bring in stdio and do
631 * a binary search on the tags file.
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
640 omagic = value(vi_MAGIC);
641 tl = value(vi_TAGLENGTH);
642 if (!skipend()) {
643 unsigned char *lp = lasttag;
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();
665 * Loop once for each file in tags "path".
667 * System tags array limits to 4k (tags[ONMSZ]) long,
668 * therefore, tagfbuf should be able to hold all tags.
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;
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++;
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"
719 if ( *lp == 0 && (iswhite(*cp) || tl > 511 || tl > 0 && lp-lasttag >= tl) ) {
721 * Found a match. Force selection to be
722 * the first possible.
724 if ( mid == bot && mid == top ) {
725 ; /* found first possible match */
727 else {
728 /* postpone final decision. */
729 top = mid;
730 continue;
733 else {
734 if ((int)*lp > (int)*cp)
735 bot = mid + 1;
736 else
737 goleft:
738 top = mid - 1;
739 continue;
742 * We found the tag. Decode the line in the file.
744 fclose(iof);
746 /* Rest of tag if abbreviated */
747 while (!iswhite(*cp))
748 cp++;
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++;
764 *lp++ = 0;
766 if (*cp == 0)
767 goto badtags;
768 if (dol != zero) {
770 * Save current position in 't for ^^ in visual.
772 names['t'-'a'] = *dot &~ 01;
773 if (inopen) {
774 extern unsigned char *ncols['z'-'a'+2];
775 extern unsigned char *cursor;
777 ncols['t'-'a'] = cursor;
780 #ifdef TAG_STACK
781 if (*savedfile) {
782 savetag((char *)savedfile);
784 #endif
785 strcpy(cmdbuf, cp);
786 if (strcmp(filebuf, savedfile) || !edited) {
787 unsigned char cmdbuf2[sizeof filebuf + 10];
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)"));
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;
813 * Look for pattern in the current file.
815 oglobp = globp;
816 globp = cmdbuf;
817 d = peekc; ungetchar(0);
818 if (samef)
819 markpr(dot);
821 * BUG: if it isn't found (user edited header
822 * line) we get left in nomagic mode.
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 */
833 * Binary search failed, so try linear search if -S is on.
834 * -S is needed for tags files that are not sorted.
838 * Avoid stdio and scan tag file linearly.
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;
852 while (*cp && *lp == *cp)
853 cp++, lp++;
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"
863 if ( *lp == 0 && (iswhite(*cp) || tl > 511 || tl > 0 && lp-lasttag >= tl) ) {
864 ; /* Found it. */
866 else {
867 /* Not this tag. Try the next */
868 continue;
871 * We found the tag. Decode the line in the file.
873 close(io);
874 /* Rest of tag if abbreviated */
875 while (!iswhite(*cp))
876 cp++;
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++;
892 *lp++ = 0;
894 if (*cp == 0)
895 goto badtags2;
896 if (dol != zero) {
898 * Save current position in 't for ^^ in visual.
900 names['t'-'a'] = *dot &~ 01;
901 if (inopen) {
902 extern unsigned char *ncols['z'-'a'+2];
903 extern unsigned char *cursor;
905 ncols['t'-'a'] = cursor;
908 #ifdef TAG_STACK
909 if (*savedfile) {
910 savetag((char *)savedfile);
912 #endif
913 strcpy(cmdbuf, cp);
914 if (strcmp(filebuf, savedfile) || !edited) {
915 unsigned char cmdbuf2[sizeof filebuf + 10];
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)"));
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;
941 * Look for pattern in the current file.
943 oglobp = globp;
944 globp = cmdbuf;
945 d = peekc; ungetchar(0);
946 if (samef)
947 markpr(dot);
949 * BUG: if it isn't found (user edited header
950 * line) we get left in nomagic mode.
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" */
961 * No such tag in this file. Close it and try the next.
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);
979 * Save lines from addr1 thru addr2 as though
980 * they had been deleted.
983 yank(void)
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);
995 * z command; print windows of text in the file.
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.
1004 bool zhadpr;
1005 bool znoclear;
1006 short zweight;
1008 void
1009 zop(int hadpr)
1011 int c, nlines, op;
1012 bool excl;
1014 zhadpr = hadpr;
1015 notempty();
1016 znoclear = 0;
1017 zweight = 0;
1018 excl = exclam();
1019 switch (c = op = getchar()) {
1021 case '^':
1022 zweight = 1;
1023 case '-':
1024 case '+':
1025 while (peekchar() == op) {
1026 ignchar();
1027 zweight++;
1029 case '=':
1030 case '.':
1031 c = getchar();
1032 break;
1034 case EOF:
1035 znoclear++;
1036 break;
1038 default:
1039 op = 0;
1040 break;
1042 if (isdigit(c)) {
1043 nlines = c - '0';
1044 for(;;) {
1045 c = getchar();
1046 if (!isdigit(c))
1047 break;
1048 nlines *= 10;
1049 nlines += c - '0';
1051 if (nlines < lines)
1052 znoclear++;
1053 value(vi_WINDOW) = nlines;
1054 if (op == '=')
1055 nlines += 2;
1057 else {
1058 nlines = op == EOF ? value(vi_SCROLL) :
1059 excl ? lines - 1 : value(vi_WINDOW);
1061 if (inopen || c != EOF) {
1062 ungetchar(c);
1063 donewline();
1065 addr1 = addr2;
1066 if (addr2 == 0 && dot < dol && op == 0)
1067 addr1 = addr2 = dot+1;
1068 setdot();
1069 zop2(nlines, op);
1072 void
1073 zop2(int nlines, int op)
1075 line *split;
1077 split = NULL;
1078 switch (op) {
1080 case EOF:
1081 if (addr2 == dol)
1082 error(gettext("\nAt EOF"));
1083 case '+':
1084 if (addr2 == dol)
1085 error(gettext("At EOF"));
1086 addr2 += nlines * zweight;
1087 if (addr2 > dol)
1088 error(gettext("Hit BOTTOM"));
1089 addr2++;
1090 default:
1091 addr1 = addr2;
1092 addr2 += nlines-1;
1093 dot = addr2;
1094 break;
1096 case '=':
1097 case '.':
1098 znoclear = 0;
1099 nlines--;
1100 nlines >>= 1;
1101 if (op == '=')
1102 nlines--;
1103 addr1 = addr2 - nlines;
1104 if (op == '=')
1105 dot = split = addr2;
1106 addr2 += nlines;
1107 if (op == '.') {
1108 markDOT();
1109 dot = addr2;
1111 break;
1113 case '^':
1114 case '-':
1115 addr2 -= nlines * zweight;
1116 if (addr2 < one)
1117 error(gettext("Hit TOP"));
1118 nlines--;
1119 addr1 = addr2 - nlines;
1120 dot = addr2;
1121 break;
1123 if (addr1 <= zero)
1124 addr1 = one;
1125 if (addr2 > dol)
1126 addr2 = dol;
1127 if (dot > dol)
1128 dot = dol;
1129 if (addr1 > addr2)
1130 return;
1131 if (op == EOF && zhadpr) {
1132 getaline(*addr1);
1133 putchar((int)('\r' | QUOTE));
1134 shudclob = 1;
1135 } else if (znoclear == 0 && clear_screen != NOSTR && !inopen) {
1136 flush1();
1137 vclear();
1139 if (addr2 - addr1 > 1)
1140 pstart();
1141 if (split) {
1142 plines(addr1, split - 1, 0);
1143 splitit();
1144 plines(split, split, 0);
1145 splitit();
1146 addr1 = split + 1;
1148 plines(addr1, addr2, 0);
1151 static void
1152 splitit(void)
1154 int l;
1156 for (l = columns > 80 ? 40 : columns / 2; l > 0; l--)
1157 putchar('-');
1158 putnl();
1161 void
1162 plines(line *adr1, line *adr2, bool movedot)
1164 line *addr;
1166 pofix();
1167 for (addr = adr1; addr <= adr2; addr++) {
1168 getaline(*addr);
1169 pline(lineno(addr));
1170 if (inopen)
1171 putchar((int)('\n' | QUOTE));
1172 if (movedot)
1173 dot = addr;
1177 void
1178 pofix(void)
1181 if (inopen && Outchar != termchar) {
1182 vnfl();
1183 setoutt();
1188 * Command level undo works easily because
1189 * the editor has a unique temporary file
1190 * index for every line which ever existed.
1191 * We don't have to save large blocks of text,
1192 * only the indices which are small. We do this
1193 * by moving them to after the last line in the
1194 * line buffer array, and marking down info
1195 * about whence they came.
1197 * Undo is its own inverse.
1199 void
1200 undo(bool c)
1202 int i, k;
1203 line *jp, *kp, *j;
1204 line *dolp1, *newdol, *newadot;
1206 #ifdef UNDOTRACE
1207 if (trace)
1208 vudump("before undo");
1209 #endif
1210 if (inglobal && inopen <= 0)
1211 error(value(vi_TERSE) ? gettext("Can't undo in global") :
1212 gettext("Can't undo in global commands"));
1215 * Unless flag indicates a forced undo, make sure
1216 * there really was a change before trying to undo it.
1219 if (!c)
1220 somechange();
1223 * Update change flags.
1226 pkill[0] = pkill[1] = 0;
1227 change();
1228 if (undkind == UNDMOVE) {
1230 * Command to be undone is a move command.
1231 * This is handled as a special case by noting that
1232 * a move "a,b m c" can be inverted by another move.
1234 if ((i = (jp = unddel) - undap2) > 0) {
1236 * when c > b inverse is a+(c-b),c m a-1
1238 addr2 = jp;
1239 addr1 = (jp = undap1) + i;
1240 unddel = jp-1;
1241 } else {
1243 * when b > c inverse is c+1,c+1+(b-a) m b
1245 addr1 = ++jp;
1246 addr2 = jp + ((unddel = undap2) - undap1);
1248 kp = undap1;
1249 move1(0, unddel);
1250 dot = kp;
1251 Command = (unsigned char *)"move";
1252 killed();
1253 } else {
1254 int cnt;
1256 newadot = dot;
1257 cnt = lineDOL();
1258 newdol = dol;
1259 dolp1 = dol + 1;
1261 * Command to be undone is a non-move.
1262 * All such commands are treated as a combination of
1263 * a delete command and a append command.
1264 * We first move the lines appended by the last command
1265 * from undap1 to undap2-1 so that they are just before the
1266 * saved deleted lines.
1268 * Assume the editor has:
1270 * cursor is on 'c'
1272 * (just change lines 5-8)
1274 * file is: 1) ab
1275 * 2) cd
1276 * 3) ef
1277 * 4) gh
1278 * undap1: 5) 12
1279 * 6) 34
1280 * 7) 56
1281 * 8) 78
1282 * undap2: 9) qr
1283 * 10) st
1284 * 11) uv
1285 * 12) wx
1286 * dol: 13) yz
1288 * UNDO AREA:
1289 * dol+1: 5) ij
1290 * 6) kl
1291 * 7) mn
1292 * unddol: 8) op
1296 * If this is a change (not a delete/put),
1297 * then we must move the text between undap1 and undap2
1298 * and it must not be at the bottom of the file
1301 if ((i = (kp = undap2) - (jp = undap1)) > 0) {
1302 if (kp != dolp1) {
1305 * FILE: LINE INITIAL REV1 REV2 REV3
1307 * 1) ab ab ab ab
1308 * 2) cd cd cd cd
1309 * 3) ef ef ef ef
1310 * unddel: 4) gh gh gh gh
1311 * undap1: 5) 12 78 78 qr
1312 * 6) 34 56 56 st
1313 * 7) 56 34 34 uv
1314 * 8) 78 12 12 wx
1315 * undap2: 9) qr qr yz yz
1316 * 10) st st wx 12
1317 * 11) uv uv uv 34
1318 * 12) wx wx st 56
1319 * dol: 13) yz yz qr 78
1321 * UNDO AREA:
1322 * dol+1: 5) ij ij ij ij
1323 * 6) kl kl kl kl
1324 * 7) mn mn mn mn
1325 * unddol: 8) op op op op
1328 reverse(jp, kp);
1329 reverse(kp, dolp1);
1330 reverse(jp, dolp1);
1333 * Unddel, the line just before the spot where this
1334 * test was deleted, may have moved. Account for
1335 * this in restoration of saved deleted lines.
1337 if (unddel >= jp)
1338 unddel -= i;
1341 * The last line (dol) may have changed,
1342 * account for this.
1344 newdol -= i;
1347 * For the case where no lines are restored, dot
1348 * is the line before the first line deleted.
1350 dot = jp-1;
1353 * Now put the deleted lines, if any, back where they were.
1354 * Basic operation is: dol+1,unddol m unddel
1356 if (undkind == UNDPUT) {
1357 unddel = undap1 - 1;
1358 squish();
1362 * Set jp to the line where deleted text is to be added.
1364 jp = unddel + 1;
1367 * Set kp to end of undo save area.
1369 * If there is any deleted text to be added, do reverses.
1372 if ((i = (kp = unddol) - dol) > 0) {
1375 * If deleted lines are not to be appended
1376 * to the bottom of the file...
1379 if (jp != dolp1) {
1381 * FILE: LINE START REV1 REV2 REV3
1382 * 1) ab ab ab ab
1383 * 2) cd cd cd cd
1384 * 3) ef ef ef ef
1385 * unddel: 4) gh gh gh gh
1386 * undap1: 5) qr 78 78 ij
1387 * 6) st 56 56 kl
1388 * 7) uv 34 34 mn
1389 * 8) wx 12 12 op
1390 * undap2: 9) yz yz yz qr
1391 * 10) 12 wx wx st
1392 * 11) 34 uv uv uv
1393 * 12) 56 st st wx
1394 * dol: 13) 78 qr qr yz
1396 * UNDO AREA:
1397 * dol+1: 5) ij ij op 12
1398 * 6) kl kl mn 34
1399 * 7) mn mn kl 56
1400 * unddol: 8) op op ij 78
1403 reverse(jp, dolp1);
1404 reverse(dolp1, ++kp);
1405 reverse(jp, kp);
1408 * Account for possible forward motion of the target
1409 * (where the deleted lines were restored) for after
1410 * restoration of the deleted lines.
1412 if (undap1 >= jp)
1413 undap1 += i;
1415 * Dot is the first resurrected line.
1417 dot = jp;
1420 * Account for a shift in the last line (dol).
1423 newdol += i;
1426 * Clean up so we are invertible
1428 unddel = undap1 - 1;
1429 undap1 = jp;
1430 undap2 = jp + i;
1431 dol = newdol;
1432 netchHAD(cnt);
1433 if (undkind == UNDALL) {
1434 dot = undadot;
1435 undadot = newadot;
1436 } else
1437 undkind = UNDCHANGE;
1439 * Now relocate all marks for lines that were modified,
1440 * since the marks point to lines whose address has
1441 * been modified from the save area to the current
1442 * area
1445 for (j=unddol; j> dol; j--)
1446 for (k=0; k<=25; k++)
1447 if (names[k] == *(j))
1448 names[k]= *((undap1+(j-dolp1)) );
1451 * Defensive programming - after a munged undadot.
1452 * Also handle empty buffer case.
1454 if ((dot <= zero || dot > dol) && dot != dol)
1455 dot = one;
1456 #ifdef UNDOTRACE
1457 if (trace)
1458 vudump("after undo");
1459 #endif
1463 * Be (almost completely) sure there really
1464 * was a change, before claiming to undo.
1466 void
1467 somechange(void)
1469 line *ip, *jp;
1471 switch (undkind) {
1473 case UNDMOVE:
1474 return;
1476 case UNDCHANGE:
1477 if (undap1 == undap2 && dol == unddol)
1478 break;
1479 return;
1481 case UNDPUT:
1482 if (undap1 != undap2)
1483 return;
1484 break;
1486 case UNDALL:
1487 if (unddol - dol != lineDOL())
1488 return;
1489 for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
1490 if ((*ip &~ 01) != (*jp &~ 01))
1491 return;
1492 break;
1494 case UNDNONE:
1495 error(gettext("Nothing to undo"));
1497 error(value(vi_TERSE) ? gettext("Nothing changed") :
1498 gettext("Last undoable command didn't change anything"));
1502 * Map command:
1503 * map src dest
1505 * un is true if this is unmap command
1506 * ab is true if this is abbr command
1508 void
1509 mapcmd(int un, int ab)
1511 unsigned char lhs[100], rhs[100]; /* max sizes resp. */
1512 unsigned char *p;
1513 int c; /* char --> int */
1514 unsigned char *dname;
1515 struct maps *mp; /* the map structure we are working on */
1517 mp = ab ? abbrevs : exclam() ? immacs : arrows;
1518 if (skipend()) {
1519 int i;
1521 /* print current mapping values */
1522 if (peekchar() != EOF)
1523 ignchar();
1524 if (un)
1525 error(gettext("Missing lhs"));
1526 if (inopen)
1527 pofix();
1528 for (i=0; i< MAXNOMACS && mp[i].mapto; i++)
1529 if (mp[i].cap) {
1530 lprintf("%s", mp[i].descr);
1531 putchar('\t');
1532 lprintf("%s", mp[i].cap);
1533 putchar('\t');
1534 lprintf("%s", mp[i].mapto);
1535 putNFL();
1537 return;
1540 (void)skipwh();
1541 for (p=lhs; ; ) {
1542 c = getchar();
1543 if (c == CTRL('v')) {
1544 c = getchar();
1545 } else if (!un && any(c, " \t")) {
1546 /* End of lhs */
1547 break;
1548 } else if (endcmd(c) && c!='"') {
1549 ungetchar(c);
1550 if (un) {
1551 donewline();
1552 *p = 0;
1553 addmac(lhs, (unsigned char *)NOSTR,
1554 (unsigned char *)NOSTR, mp);
1555 return;
1556 } else
1557 error(gettext("Missing rhs"));
1559 *p++ = c;
1561 *p = 0;
1563 if (skipend())
1564 error(gettext("Missing rhs"));
1565 for (p=rhs; ; ) {
1566 c = getchar();
1567 if (c == CTRL('v')) {
1568 c = getchar();
1569 } else if (endcmd(c) && c!='"') {
1570 ungetchar(c);
1571 break;
1573 *p++ = c;
1575 *p = 0;
1576 donewline();
1578 * Special hack for function keys: #1 means key f1, etc.
1579 * If the terminal doesn't have function keys, we just use #1.
1581 if (lhs[0] == '#') {
1582 unsigned char *fnkey;
1583 unsigned char *fkey();
1584 unsigned char funkey[3];
1586 fnkey = fkey(lhs[1] - '0');
1587 funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
1588 if (fnkey)
1589 strcpy(lhs, fnkey);
1590 dname = funkey;
1591 } else {
1592 dname = lhs;
1594 addmac(lhs,rhs,dname,mp);
1598 * Add a macro definition to those that already exist. The sequence of
1599 * chars "src" is mapped into "dest". If src is already mapped into something
1600 * this overrides the mapping. There is no recursion. Unmap is done by
1601 * using NOSTR for dest. Dname is what to show in listings. mp is
1602 * the structure to affect (arrows, etc).
1604 void
1605 addmac(unsigned char *src, unsigned char *dest, unsigned char *dname,
1606 struct maps *mp)
1608 int slot, zer;
1610 #ifdef UNDOTRACE
1611 if (trace)
1612 fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
1613 #endif
1614 if (dest && mp==arrows) {
1616 * Prevent tail recursion. We really should be
1617 * checking to see if src is a suffix of dest
1618 * but this makes mapping involving escapes that
1619 * is reasonable mess up.
1621 if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
1622 error(gettext("No tail recursion"));
1624 * We don't let the user rob themself of ":", and making
1625 * multi char words is a bad idea so we don't allow it.
1626 * Note that if user sets mapinput and maps all of return,
1627 * linefeed, and escape, they can hurt themself. This is
1628 * so weird I don't bother to check for it.
1630 if (isalpha(src[0]) && isascii(src[0]) && src[1] || any(src[0],":"))
1631 error(gettext("Too dangerous to map that"));
1633 else if (dest) {
1634 /* check for tail recursion in input mode: fussier */
1635 if (eq(src, dest+strlen(dest)-strlen(src)))
1636 error(gettext("No tail recursion"));
1639 * If the src were null it would cause the dest to
1640 * be mapped always forever. This is not good.
1642 if (src == (unsigned char *)NOSTR || src[0] == 0)
1643 error(gettext("Missing lhs"));
1645 /* see if we already have a def for src */
1646 zer = -1;
1647 for (slot=0; slot < MAXNOMACS && mp[slot].mapto; slot++) {
1648 if (mp[slot].cap) {
1649 if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
1650 break; /* if so, reuse slot */
1651 } else {
1652 zer = slot; /* remember an empty slot */
1656 if (slot >= MAXNOMACS)
1657 error(gettext("Too many macros"));
1659 if (dest == (unsigned char *)NOSTR) {
1660 /* unmap */
1661 if (mp[slot].cap) {
1662 mp[slot].cap = (unsigned char *)NOSTR;
1663 mp[slot].descr = (unsigned char *)NOSTR;
1664 } else {
1665 error(value(vi_TERSE) ? gettext("Not mapped") :
1666 gettext("That macro wasn't mapped"));
1668 return;
1671 /* reuse empty slot, if we found one and src isn't already defined */
1672 if (zer >= 0 && mp[slot].mapto == 0)
1673 slot = zer;
1675 /* if not, append to end */
1676 if (msnext == 0) /* first time */
1677 msnext = mapspace;
1678 /* Check is a bit conservative, we charge for dname even if reusing src */
1679 if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS)
1680 error(gettext("Too much macro text"));
1681 CP(msnext, src);
1682 mp[slot].cap = msnext;
1683 msnext += strlen(src) + 1; /* plus 1 for null on the end */
1684 CP(msnext, dest);
1685 mp[slot].mapto = msnext;
1686 msnext += strlen(dest) + 1;
1687 if (dname) {
1688 CP(msnext, dname);
1689 mp[slot].descr = msnext;
1690 msnext += strlen(dname) + 1;
1691 } else {
1692 /* default descr to string user enters */
1693 mp[slot].descr = src;
1698 * Implements macros from command mode. c is the buffer to
1699 * get the macro from.
1701 void
1702 cmdmac(c)
1703 unsigned char c;
1705 unsigned char macbuf[BUFSIZE];
1706 line *ad, *a1, *a2;
1707 unsigned char *oglobp;
1708 short pk;
1709 bool oinglobal;
1711 lastmac = c;
1712 oglobp = globp;
1713 oinglobal = inglobal;
1714 pk = peekc; peekc = 0;
1715 if (inglobal < 2)
1716 inglobal = 1;
1717 regbuf(c, macbuf, sizeof(macbuf));
1718 a1 = addr1; a2 = addr2;
1719 for (ad=a1; ad<=a2; ad++) {
1720 globp = macbuf;
1721 dot = ad;
1722 commands(1,1);
1724 globp = oglobp;
1725 inglobal = oinglobal;
1726 peekc = pk;
1729 unsigned char *
1730 vgetpass(prompt)
1731 char *prompt;
1733 unsigned char *p;
1734 int c;
1735 static unsigned char pbuf[9];
1737 /* In ex mode, let the system hassle with setting no echo */
1738 if (!inopen)
1739 return (unsigned char *)getpass(prompt);
1740 viprintf("%s", prompt); flush();
1741 for (p=pbuf; (c = getkey())!='\n' && c!=EOF && c!='\r';) {
1742 if (p < &pbuf[8])
1743 *p++ = c;
1745 *p = '\0';
1746 return(pbuf);
1750 #ifdef TAG_STACK
1751 #define TSTACKSIZE 20
1752 struct tagstack {
1753 line *tag_line;
1754 char *tag_file;
1755 } tagstack[TSTACKSIZE];
1756 static int tag_depth = 0;
1758 static char tag_buf[ 1024 ];
1759 static char *tag_end = tag_buf;
1761 void
1762 savetag(char *name) /* saves location where we are */
1764 if( !value(vi_TAGSTACK) )
1765 return;
1766 if(tag_depth >= TSTACKSIZE) {
1767 error(gettext("Tagstack too deep."));
1769 if( strlen( name ) + 1 + tag_end >= &tag_buf[1024]) {
1770 error(gettext("Too many tags."));
1772 tagstack[tag_depth].tag_line = dot;
1773 tagstack[tag_depth++].tag_file = tag_end;
1774 while(*tag_end++ = *name++)
1779 * Undo a "savetag".
1781 void
1782 unsavetag(void)
1784 if (!value(vi_TAGSTACK))
1785 return;
1786 if (tag_depth > 0)
1787 tag_end = tagstack[--tag_depth].tag_file;
1790 void
1791 poptag(quick) /* puts us back where we came from */
1792 bool quick;
1794 unsigned char cmdbuf[100];
1795 unsigned char *oglobp;
1796 int d;
1798 if (!value(vi_TAGSTACK)) { /* reset the stack */
1799 tag_end = tag_buf;
1800 d = tag_depth;
1801 tag_depth = 0;
1802 if (d == 0)
1803 error(gettext("Tagstack not enabled."));
1804 else
1805 return;
1807 if (!tag_depth)
1808 error(gettext("Tagstack empty."));
1810 /* change to old file */
1811 if (strcmp(tagstack[tag_depth-1].tag_file, savedfile) ) {
1812 if (!quick) {
1813 ckaw();
1814 if (chng && dol > zero)
1815 error(value(vi_TERSE) ?
1816 gettext("No write") : gettext("No write since last change (:pop! overrides)"));
1818 oglobp = globp;
1819 strcpy(cmdbuf, "e! ");
1820 strcat(cmdbuf, tagstack[tag_depth-1].tag_file);
1821 globp = cmdbuf;
1822 d = peekc; ungetchar(0);
1823 commands(1, 1);
1824 peekc = d;
1825 globp = oglobp;
1827 markpr(dot);
1828 /* set line number */
1829 dot = tagstack[--tag_depth].tag_line;
1830 tag_end = tagstack[tag_depth].tag_file;
1832 #endif