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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30
31 /* Copyright (c) 1981 Regents of the University of California */
32
33 #pragma ident "%Z%%M% %I% %E% SMI"
34
35 #include "ex.h"
36 #include "ex_tty.h"
37 #include "ex_vis.h"
38
39 /*
40 * Deal with the screen, clearing, cursor positioning, putting characters
41 * into the screen image, and deleting characters.
42 * Really hard stuff here is utilizing insert character operations
43 * on intelligent terminals which differs widely from terminal to terminal.
44 */
45 void
vclear(void)46 vclear(void)
47 {
48
49 #ifdef TRACE
50 if (trace)
51 tfixnl(), fprintf(trace, "------\nvclear, clear_screen '%s'\n", clear_screen);
52 #endif
53 tputs(clear_screen, lines, putch);
54 destcol = 0;
55 outcol = 0;
56 destline = 0;
57 outline = 0;
58 if (inopen)
59 vclrbyte(vtube0, WCOLS * (WECHO - ZERO + 1));
60 }
61
62 /*
63 * Clear memory.
64 */
65 void
vclrbyte(wchar_t * cp,int i)66 vclrbyte(wchar_t *cp, int i)
67 {
68
69 if (i > 0)
70 do
71 *cp++ = 0;
72 while (--i != 0);
73 }
74
75 /*
76 * Clear a physical display line, high level.
77 */
78 void
vclrlin(int l,line * tp)79 vclrlin(int l, line *tp)
80 {
81
82 vigoto(l, 0);
83 if ((hold & HOLDAT) == 0)
84 putchar(tp > dol ? ((UPPERCASE || tilde_glitch) ? '^' : '~') : '@');
85 if (state == HARDOPEN)
86 sethard();
87 vclreol();
88 }
89
90 /*
91 * Clear to the end of the current physical line
92 */
93 void
vclreol(void)94 vclreol(void)
95 {
96 int i;
97 wchar_t *tp, j;
98
99 #ifdef TRACE
100 if (trace)
101 fprintf(trace, "vclreol(), destcol %d, ateopr() %d\n", destcol, ateopr());
102 #endif
103 if (destcol == WCOLS)
104 return;
105 destline += destcol / WCOLS;
106 destcol %= WCOLS;
107 if (destline < 0 || destline > WECHO)
108 error(gettext("Internal error: vclreol"));
109 i = WCOLS - destcol;
110 tp = vtube[destline] + destcol;
111 if (clr_eol) {
112 if (insert_null_glitch && *tp || !ateopr()) {
113 vcsync();
114 vputp(clr_eol, 1);
115 }
116 vclrbyte(tp, i);
117 return;
118 }
119 if (*tp == 0)
120 return;
121 while (i > 0 && (j = *tp & (QUOTE|TRIM))) {
122 if (j != ' ' && (j & QUOTE) == 0) {
123 destcol = WCOLS - i;
124 (void) vputchar(' ');
125 }
126 --i, *tp++ = 0;
127 }
128 }
129
130 /*
131 * Clear the echo line.
132 * If didphys then its been cleared physically (as
133 * a side effect of a clear to end of display, e.g.)
134 * so just do it logically.
135 * If work here is being held off, just remember, in
136 * heldech, if work needs to be done, don't do anything.
137 */
138 void
vclrech(didphys)139 vclrech(didphys)
140 bool didphys;
141 {
142
143 #ifdef ADEBUG
144 if (trace)
145 fprintf(trace, "vclrech(%d), Peekkey %d, hold %o\n", didphys, Peekkey, hold);
146 #endif
147 if (Peekkey == ATTN)
148 return;
149 if (hold & HOLDECH) {
150 heldech = !didphys;
151 return;
152 }
153 if (!didphys && (clr_eos || clr_eol)) {
154 splitw++;
155 /*
156 * If display is retained below, then MUST use clr_eos or
157 * clr_eol since we don't really know whats out there.
158 * Vigoto might decide (incorrectly) to do nothing.
159 */
160 if (memory_below) {
161 vgoto(WECHO, 0);
162 /*
163 * This is tricky. If clr_eos is as cheap we
164 * should use it, so we don't have extra junk
165 * floating around in memory below. But if
166 * clr_eol costs less we should use it. The real
167 * reason here is that clr_eos is incredibly
168 * expensive on the HP 2626 (1/2 second or more)
169 * which makes ^D scroll really slow. But the
170 * 2621 has a bug that shows up if we use clr_eol
171 * instead of clr_eos, so we make sure the costs
172 * are equal so it will prefer clr_eol.
173 */
174 if (costCE < costCD)
175 vputp(clr_eol, 1);
176 else
177 vputp(clr_eos, 1);
178 } else {
179 if (teleray_glitch) {
180 /* This code basically handles the t1061
181 * where positioning at (0, 0) won't work
182 * because the terminal won't let you put
183 * the cursor on it's magic cookie.
184 *
185 * Should probably be ceol_standout_glitch
186 * above, or even a
187 * new glitch, but right now t1061 is the
188 * only terminal with teleray_glitch.
189 */
190 vgoto(WECHO, 0);
191 vputp(delete_line, 1);
192 } else {
193 vigoto(WECHO, 0);
194 vclreol();
195 }
196 }
197 splitw = 0;
198 didphys = 1;
199 }
200 if (didphys)
201 vclrbyte(vtube[WECHO], WCOLS);
202 heldech = 0;
203 }
204
205 /*
206 * Fix the echo area for use, setting
207 * the state variable splitw so we wont rollup
208 * when we move the cursor there.
209 */
210 void
fixech(void)211 fixech(void)
212 {
213
214 splitw++;
215 if (state != VISUAL && state != CRTOPEN) {
216 vclean();
217 vcnt = 0;
218 }
219 vgoto(WECHO, 0); flusho();
220 }
221
222 /*
223 * Put the cursor ``before'' cp.
224 */
225 void
vcursbef(unsigned char * cp)226 vcursbef(unsigned char *cp)
227 {
228
229 if (cp <= linebuf)
230 vgotoCL(value(vi_NUMBER) << 3);
231 else
232 vgotoCL(lcolumn(cp)-1);
233 }
234
235 /*
236 * Put the cursor ``at'' cp.
237 */
238 void
vcursat(unsigned char * cp)239 vcursat(unsigned char *cp)
240 {
241
242 if (cp <= linebuf && linebuf[0] == 0)
243 vgotoCL(value(vi_NUMBER) << 3);
244 else
245 vgotoCL(lcolumn(cp));
246 }
247
248 /*
249 * Put the cursor ``after'' cp.
250 */
251 void
vcursaft(unsigned char * cp)252 vcursaft(unsigned char *cp)
253 {
254
255 vgotoCL(lcolumn(nextchr(cp)));
256 }
257
258 /*
259 * Fix the cursor to be positioned in the correct place
260 * to accept a command.
261 */
262 void
vfixcurs(void)263 vfixcurs(void)
264 {
265
266 vsetcurs(cursor);
267 }
268
269 /*
270 * Compute the column position implied by the cursor at ``nc'',
271 * and move the cursor there.
272 */
273 void
vsetcurs(unsigned char * nc)274 vsetcurs(unsigned char *nc)
275 {
276 int col;
277
278 col = column(nc);
279 if (linebuf[0])
280 col--;
281 vgotoCL(col);
282 cursor = nc;
283 }
284
285 /*
286 * Move the cursor invisibly, i.e. only remember to do it.
287 */
288 void
vigoto(int y,int x)289 vigoto(int y, int x)
290 {
291
292 destline = y;
293 destcol = x;
294 }
295
296 /*
297 * Move the cursor to the position implied by any previous
298 * vigoto (or low level hacking with destcol/destline as in readecho).
299 */
300 void
vcsync(void)301 vcsync(void)
302 {
303
304 vgoto(destline, destcol);
305 }
306
307 /*
308 * Goto column x of the current line.
309 */
310 void
vgotoCL(int x)311 vgotoCL(int x)
312 {
313
314 if (splitw)
315 vgoto(WECHO, x);
316 else
317 vgoto(LINE(vcline), x);
318 }
319
320 /*
321 * Invisible goto column x of current line.
322 */
323 void
vigotoCL(int x)324 vigotoCL(int x)
325 {
326
327 if (splitw)
328 vigoto(WECHO, x);
329 else
330 vigoto(LINE(vcline), x);
331 }
332
333 /*
334 * Show the current mode in the right hand part of the echo line,
335 * then return the cursor to where it is now.
336 */
337 void
vshowmode(unsigned char * msg)338 vshowmode(unsigned char *msg)
339 {
340 int savecol, saveline, savesplit;
341 unsigned char *p;
342 wchar_t wchar;
343 int length;
344
345 if (!value(vi_SHOWMODE))
346 return;
347 /* Don't alter mode message for macros (arrow keys) or yank/put */
348 if (vmacp || vglobp)
349 return;
350 savecol = outcol; saveline = outline; savesplit = splitw;
351 splitw = 1; /* To avoid scrolling */
352 vigoto(WECHO, WCOLS-20);
353
354 if (*msg) {
355 vcsync();
356 for (p = msg; *p;) {
357 length = mbtowc(&wchar, (char *)p, MULTI_BYTE_MAX);
358 if (length <= 0) {
359 /*
360 * This should never happen, but
361 * if 'msg' doesn't make a valid string,
362 * treat this case as the same as the
363 * null string 'msg'.
364 */
365 /*
366 * Going back to command mode - clear the message.
367 */
368 vclreol();
369 break;
370 } else {
371 (void) vputchar(wchar);
372 p += length;
373 }
374 }
375 } else {
376 /*
377 * Going back to command mode - clear the message.
378 */
379 vclreol();
380 }
381
382 FLAGS(WECHO) |= VDIRT;
383 vgoto(saveline, savecol);
384 splitw = savesplit;
385 }
386
387 /*
388 * Move cursor to line y, column x, handling wraparound and scrolling.
389 */
390 void
vgoto(int y,int x)391 vgoto(int y, int x)
392 {
393 wchar_t *tp;
394 wchar_t c;
395 int col;
396
397 /*
398 * Fold the possibly too large value of x.
399 */
400 if (x >= WCOLS) {
401 y += x / WCOLS;
402 x %= WCOLS;
403 }
404 if (y < 0) {
405 error("Internal error: vgoto");
406 }
407 if (outcol >= WCOLS) {
408 if (auto_right_margin) {
409 outline += outcol / WCOLS;
410 outcol %= WCOLS;
411 } else
412 outcol = WCOLS - 1;
413 }
414
415 /*
416 * In a hardcopy or glass crt open, print the stuff
417 * implied by a motion, or backspace.
418 */
419 if (state == HARDOPEN || state == ONEOPEN) {
420 if (y != outline)
421 error(gettext("Line too long for open"));
422 if (x + 1 < outcol - x || (outcol > x && !cursor_left))
423 destcol = 0, fgoto();
424 tp = vtube[WBOT] + outcol;
425 while (outcol != x)
426 if (outcol < x) {
427 int length;
428 unsigned char multic[MULTI_BYTE_MAX];
429 if (*tp == 0)
430 *tp = ' ';
431 c = *tp++ & TRIM;
432 length = wctomb((char *)multic, c);
433 if(length == 0)
434 length = 1;
435 while(length--)
436 (void) vputc(c &&
437 (!over_strike || erase_overstrike)
438 ? c : ' ');
439 if (c) {
440 if ((col = wcwidth(c)) < 0)
441 col = 0;
442 } else
443 col = 1;
444 outcol += col;
445 } else {
446 vputp(cursor_left, 0);
447 outcol--;
448 }
449 destcol = outcol = x;
450 destline = outline;
451 return;
452 }
453
454 /*
455 * If the destination position implies a scroll, do it.
456 */
457 destline = y;
458 if (destline > WBOT && (!splitw || destline > WECHO)) {
459 endim();
460 vrollup(destline);
461 }
462
463 /*
464 * If there really is a motion involved, do it.
465 * The check here is an optimization based on profiling.
466 */
467 destcol = x;
468 if ((destline - outline) * WCOLS != destcol - outcol) {
469 if (!move_insert_mode)
470 endim();
471 fgoto();
472 }
473 }
474
475 /*
476 * This is the hardest code in the editor, and deals with insert modes
477 * on different kinds of intelligent terminals. The complexity is due
478 * to the cross product of three factors:
479 *
480 * 1. Lines may display as more than one segment on the screen.
481 * 2. There are 2 kinds of intelligent terminal insert modes.
482 * 3. Tabs squash when you insert characters in front of them,
483 * in a way in which current intelligent terminals don't handle.
484 *
485 * The two kinds of terminals are typified by the DM2500 or HP2645 for
486 * one and the CONCEPT-100 or the FOX for the other.
487 *
488 * The first (HP2645) kind has an insert mode where the characters
489 * fall off the end of the line and the screen is shifted rigidly
490 * no matter how the display came about.
491 *
492 * The second (CONCEPT-100) kind comes from terminals which are designed
493 * for forms editing and which distinguish between blanks and ``spaces''
494 * on the screen, spaces being like blank, but never having had
495 * and data typed into that screen position (since, e.g. a clear operation
496 * like clear screen). On these terminals, when you insert a character,
497 * the characters from where you are to the end of the screen shift
498 * over till a ``space'' is found, and the null character there gets
499 * eaten up.
500 *
501 *
502 * The code here considers the line as consisting of several parts
503 * the first part is the ``doomed'' part, i.e. a part of the line
504 * which is being typed over. Next comes some text up to the first
505 * following tab. The tab is the next segment of the line, and finally
506 * text after the tab.
507 *
508 * We have to consider each of these segments and the effect of the
509 * insertion of a character on them. On terminals like HP2645's we
510 * must simulate a multi-line insert mode using the primitive one
511 * line insert mode. If we are inserting in front of a tab, we have
512 * to either delete characters from the tab or insert white space
513 * (when the tab reaches a new spot where it gets larger) before we
514 * insert the new character.
515 *
516 * On a terminal like a CONCEPT our strategy is to make all
517 * blanks be displayed, while trying to keep the screen having ``spaces''
518 * for portions of tabs. In this way the terminal hardware does some
519 * of the hacking for compression of tabs, although this tends to
520 * disappear as you work on the line and spaces change into blanks.
521 *
522 * There are a number of boundary conditions (like typing just before
523 * the first following tab) where we can avoid a lot of work. Most
524 * of them have to be dealt with explicitly because performance is
525 * much, much worse if we don't.
526 *
527 * A final thing which is hacked here is two flavors of insert mode.
528 * Datamedia's do this by an insert mode which you enter and leave
529 * and by having normal motion character operate differently in this
530 * mode, notably by having a newline insert a line on the screen in
531 * this mode. This generally means it is unsafe to move around
532 * the screen ignoring the fact that we are in this mode.
533 * This is possible on some terminals, and wins big (e.g. HP), so
534 * we encode this as a ``can move in insert capability'' mi,
535 * and terminals which have it can do insert mode with much less
536 * work when tabs are present following the cursor on the current line.
537 */
538
539 /*
540 * Routine to expand a tab, calling the normal Outchar routine
541 * to put out each implied character. Note that we call outchar
542 * with a QUOTE. We use QUOTE internally to represent a position
543 * which is part of the expansion of a tab.
544 */
545 void
vgotab(void)546 vgotab(void)
547 {
548 int i = tabcol(destcol, value(vi_TABSTOP)) - destcol;
549
550 do
551 (*Outchar)(QUOTE);
552 while (--i);
553 }
554
555 /*
556 * Variables for insert mode.
557 */
558 int linend; /* The column position of end of line */
559 int tabstart; /* Column of start of first following tab */
560 int tabend; /* Column of end of following tabs */
561 int tabsize; /* Size of the following tabs */
562 int tabslack; /* Number of ``spaces'' in following tabs */
563 int inssiz; /* Number of characters to be inserted */
564 int inscol; /* Column where insertion is taking place */
565 int shft; /* Amount tab expansion shifted rest of line */
566 int slakused; /* This much of tabslack will be used up */
567
568 /*
569 * This routine MUST be called before insert mode is run,
570 * and brings all segments of the current line to the top
571 * of the screen image buffer so it is easier for us to
572 * manipulate them.
573 */
574 void
vprepins(void)575 vprepins(void)
576 {
577 int i;
578 wchar_t *cp = vtube0;
579
580 for (i = 0; i < DEPTH(vcline); i++) {
581 vmaktop(LINE(vcline) + i, cp);
582 cp += WCOLS;
583 }
584 }
585
586 void
vmaktop(int p,wchar_t * cp)587 vmaktop(int p, wchar_t *cp)
588 {
589 int i;
590 wchar_t temp[TUBECOLS];
591
592 if (p < 0 || vtube[p] == cp)
593 return;
594 for (i = ZERO; i <= WECHO; i++)
595 if (vtube[i] == cp) {
596 copy(temp, vtube[i], WCOLS * sizeof(wchar_t));
597 copy(vtube[i], vtube[p], WCOLS * sizeof(wchar_t));
598 copy(vtube[p], temp, WCOLS * sizeof(wchar_t));
599 vtube[i] = vtube[p];
600 vtube[p] = cp;
601 return;
602 }
603 error(gettext("Line too long"));
604 }
605
606 /*
607 * Insert character c at current cursor position.
608 * Multi-character inserts occur only as a result
609 * of expansion of tabs (i.e. inssize == 1 except
610 * for tabs or multibyte characters)
611 * and code assumes this in several place
612 * to make life simpler.
613 */
614 int
vinschar(wchar_t c)615 vinschar(wchar_t c)
616 {
617 int i;
618 wchar_t *tp, wchar;
619
620 if ((!enter_insert_mode || !exit_insert_mode) && ((hold & HOLDQIK) || !value(vi_REDRAW) || value(vi_SLOWOPEN))) {
621 /*
622 * Don't want to try to use terminal
623 * insert mode, or to try to fake it.
624 * Just put the character out; the screen
625 * will probably be wrong but we will fix it later.
626 */
627 if (c == '\t') {
628 vgotab();
629 return (0);
630 }
631 (void) vputchar(c);
632 if (DEPTH(vcline) * WCOLS + !value(vi_REDRAW) >
633 (destline - LINE(vcline)) * WCOLS + destcol)
634 return (0);
635 /*
636 * The next line is about to be clobbered
637 * make space for another segment of this line
638 * (on an intelligent terminal) or just remember
639 * that next line was clobbered (on a dumb one
640 * if we don't care to redraw the tail.
641 */
642 if (insert_line) {
643 vnpins(0);
644 } else {
645 int i2 = LINE(vcline) + DEPTH(vcline);
646 if (i2 < LINE(vcline + 1) || i2 > WBOT)
647 return (0);
648 i = destcol;
649 vinslin(i2, 1, vcline);
650 DEPTH(vcline)++;
651 vigoto(i2, i);
652 vprepins();
653 }
654 return (0);
655 }
656
657 /*
658 * Compute the number of positions in the line image of the
659 * current line. This is done from the physical image
660 * since that is faster. Note that we have no memory
661 * from insertion to insertion so that routines which use
662 * us don't have to worry about moving the cursor around.
663 */
664 if (*vtube0 == 0)
665 linend = 0;
666 else {
667 /*
668 * Search backwards for a non-null character
669 * from the end of the displayed line.
670 */
671 i = WCOLS * DEPTH(vcline);
672 if (i == 0)
673 i = WCOLS;
674 tp = vtube0 + i;
675 while (*--tp == 0)
676 if (--i == 0)
677 break;
678 linend = i;
679 }
680
681 /*
682 * We insert at a position based on the physical location
683 * of the output cursor.
684 */
685 inscol = destcol + (destline - LINE(vcline)) * WCOLS;
686 if (c == '\t') {
687 /*
688 * Characters inserted from a tab must be
689 * remembered as being part of a tab, but we can't
690 * use QUOTE here since we really need to print blanks.
691 * QUOTE|' ' is the representation of this.
692 */
693 inssiz = tabcol(inscol, value(vi_TABSTOP)) - inscol;
694 c = ' ' | QUOTE;
695 } else {
696 if ((inssiz = wcwidth(c)) < 0)
697 inssiz = 0;
698 }
699
700 /*
701 * If the text to be inserted is less than the number
702 * of doomed positions, then we don't need insert mode,
703 * rather we can just typeover.
704 */
705 if (inssiz <= doomed) {
706 endim();
707 if (inscol != linend)
708 doomed -= inssiz;
709 do {
710 (void) vputchar(c);
711 if(c & QUOTE)
712 inssiz--;
713 else
714 break;
715 } while (inssiz);
716 return (0);
717 }
718
719 /*
720 * Have to really do some insertion, thus
721 * stake out the bounds of the first following
722 * group of tabs, computing starting position,
723 * ending position, and the number of ``spaces'' therein
724 * so we can tell how much it will squish.
725 */
726 tp = vtube0 + inscol;
727 for (i = inscol; i < linend; i++)
728 if (*tp++ & QUOTE) {
729 --tp;
730 break;
731 }
732 tabstart = tabend = i;
733 tabslack = 0;
734 while (tabend < linend) {
735 wchar = *tp++;
736 if ((wchar & QUOTE) == 0)
737 break;
738 if ((wchar & TRIM) == 0)
739 tabslack++;
740 tabsize++;
741 tabend++;
742 }
743 tabsize = tabend - tabstart;
744
745 /*
746 * For HP's and DM's, e.g. tabslack has no meaning.
747 */
748 if (!insert_null_glitch)
749 tabslack = 0;
750 #ifdef IDEBUG
751 if (trace) {
752 fprintf(trace, "inscol %d, inssiz %d, tabstart %d, ",
753 inscol, inssiz, tabstart);
754 fprintf(trace, "tabend %d, tabslack %d, linend %d\n",
755 tabend, tabslack, linend);
756 }
757 #endif
758
759 /*
760 * The real work begins.
761 */
762 slakused = 0;
763 shft = 0;
764 if (tabsize) {
765 /*
766 * There are tabs on this line.
767 * If they need to expand, then the rest of the line
768 * will have to be shifted over. In this case,
769 * we will need to make sure there are no ``spaces''
770 * in the rest of the line (on e.g. CONCEPT-100)
771 * and then grab another segment on the screen if this
772 * line is now deeper. We then do the shift
773 * implied by the insertion.
774 */
775 if (inssiz >= doomed + tabcol(tabstart, value(vi_TABSTOP)) - tabstart) {
776 if (insert_null_glitch)
777 vrigid();
778 vneedpos(value(vi_TABSTOP));
779 vishft();
780 }
781 } else if (inssiz > doomed)
782 /*
783 * No tabs, but line may still get deeper.
784 */
785 vneedpos(inssiz - doomed);
786 /*
787 * Now put in the inserted characters.
788 */
789 viin(c);
790
791 /*
792 * Now put the cursor in its final resting place.
793 */
794 destline = LINE(vcline);
795 destcol = inscol + inssiz;
796 vcsync();
797 return (0);
798 }
799
800 /*
801 * Rigidify the rest of the line after the first
802 * group of following tabs, typing blanks over ``spaces''.
803 */
804 void
vrigid(void)805 vrigid(void)
806 {
807 int col;
808 wchar_t *tp = vtube0 + tabend;
809
810 for (col = tabend; col < linend; col++)
811 if ((*tp++ & TRIM) == 0) {
812 endim();
813 vgotoCL(col);
814 (void) vputchar(' ' | QUOTE);
815 }
816 }
817
818 /*
819 * We need cnt more positions on this line.
820 * Open up new space on the screen (this may in fact be a
821 * screen rollup).
822 *
823 * On a dumb terminal we may infact redisplay the rest of the
824 * screen here brute force to keep it pretty.
825 */
826 void
vneedpos(int cnt)827 vneedpos(int cnt)
828 {
829 int d = DEPTH(vcline);
830 int rmdr = d * WCOLS - linend;
831 if (cnt <= rmdr - insert_null_glitch)
832 return;
833 endim();
834 vnpins(1);
835 }
836
837 void
vnpins(int dosync)838 vnpins(int dosync)
839 {
840 int d = DEPTH(vcline);
841 int e;
842
843 e = LINE(vcline) + DEPTH(vcline);
844 if (e < LINE(vcline + 1)) {
845 vigoto(e, 0);
846 vclreol();
847 return;
848 }
849 DEPTH(vcline)++;
850 if (e < WECHO) {
851 e = vglitchup(vcline, d);
852 vigoto(e, 0); vclreol();
853 if (dosync) {
854 int (*Ooutchar)() = Outchar;
855 Outchar = vputchar;
856 vsync(e + 1);
857 Outchar = Ooutchar;
858 }
859 } else {
860 vup1();
861 vigoto(WBOT, 0);
862 vclreol();
863 }
864 vprepins();
865 }
866
867 /*
868 * Do the shift of the next tabstop implied by
869 * insertion so it expands.
870 */
871 void
vishft(void)872 vishft(void)
873 {
874 int tshft = 0;
875 int j;
876 int i;
877 wchar_t *tp = vtube0;
878 wchar_t *up, wchar;
879 short oldhold = hold;
880
881 shft = value(vi_TABSTOP);
882 hold |= HOLDPUPD;
883 if (!enter_insert_mode && !exit_insert_mode) {
884 /*
885 * Dumb terminals are easy, we just have
886 * to retype the text.
887 */
888 vigotoCL(tabend + shft);
889 up = tp + tabend;
890 for (i = tabend; i < linend; i++)
891 if((wchar = *up++) != FILLER)
892 (void) vputchar(wchar);
893 } else if (insert_null_glitch) {
894 /*
895 * CONCEPT-like terminals do most of the work for us,
896 * we don't have to muck with simulation of multi-line
897 * insert mode. Some of the shifting may come for free
898 * also if the tabs don't have enough slack to take up
899 * all the inserted characters.
900 */
901 i = shft;
902 slakused = inssiz - doomed;
903 if (slakused > tabslack) {
904 i -= slakused - tabslack;
905 slakused -= tabslack;
906 }
907 if (i > 0 && tabend != linend) {
908 tshft = i;
909 vgotoCL(tabend);
910 goim();
911 do
912 (void) vputchar(' ' | QUOTE);
913 while (--i);
914 }
915 } else {
916 /*
917 * HP and Datamedia type terminals have to have multi-line
918 * insert faked. Hack each segment after where we are
919 * (going backwards to where we are.) We then can
920 * hack the segment where the end of the first following
921 * tab group is.
922 */
923 for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) {
924 vgotoCL(j * WCOLS);
925 goim();
926 up = tp + j * WCOLS - shft;
927 i = shft;
928 do {
929 wchar_t wchar;
930 if (wchar = *up) {
931 if(wchar != FILLER)
932 (void) vputchar(wchar);
933 up++;
934 } else
935 break;
936 } while (--i);
937 }
938 vigotoCL(tabstart);
939 i = shft - (inssiz - doomed);
940 if (i > 0) {
941 tabslack = inssiz - doomed;
942 vcsync();
943 goim();
944 do
945 (void) vputchar(' ');
946 while (--i);
947 }
948 }
949 /*
950 * Now do the data moving in the internal screen
951 * image which is common to all three cases.
952 */
953 tp += linend;
954 up = tp + shft;
955 i = linend - tabend;
956 if (i > 0)
957 do
958 *--up = *--tp;
959 while (--i);
960 if (insert_null_glitch && tshft) {
961 i = tshft;
962 do
963 *--up = ' ' | QUOTE;
964 while (--i);
965 }
966 hold = oldhold;
967 }
968
969 /*
970 * Now do the insert of the characters (finally).
971 */
972 void
viin(wchar_t c)973 viin(wchar_t c)
974 {
975 wchar_t *tp, *up;
976 int i, j;
977 bool noim = 0;
978 int remdoom;
979 short oldhold = hold;
980
981 hold |= HOLDPUPD;
982 if (tabsize && (enter_insert_mode && exit_insert_mode) && inssiz - doomed > tabslack)
983 /*
984 * There is a tab out there which will be affected
985 * by the insertion since there aren't enough doomed
986 * characters to take up all the insertion and we do
987 * have insert mode capability.
988 */
989 if (inscol + doomed == tabstart) {
990 /*
991 * The end of the doomed characters sits right at the
992 * start of the tabs, then we don't need to use insert
993 * mode; unless the tab has already been expanded
994 * in which case we MUST use insert mode.
995 */
996 slakused = 0;
997 noim = !shft;
998 } else {
999 /*
1000 * The last really special case to handle is case
1001 * where the tab is just sitting there and doesn't
1002 * have enough slack to let the insertion take
1003 * place without shifting the rest of the line
1004 * over. In this case we have to go out and
1005 * delete some characters of the tab before we start
1006 * or the answer will be wrong, as the rest of the
1007 * line will have been shifted. This code means
1008 * that terminals with only insert character (no
1009 * delete character) won't work correctly.
1010 */
1011 i = inssiz - doomed - tabslack - slakused;
1012 i %= value(vi_TABSTOP);
1013 if (i > 0) {
1014 vgotoCL(tabstart);
1015 godm();
1016 for (i = inssiz - doomed - tabslack; i > 0; i--)
1017 vputp(delete_character, DEPTH(vcline));
1018 enddm();
1019 }
1020 }
1021
1022 /*
1023 * Now put out the characters of the actual insertion.
1024 */
1025 vigotoCL(inscol);
1026 remdoom = doomed;
1027 for (i = inssiz; i > 0; i--) {
1028 if (remdoom > 0) {
1029 remdoom--;
1030 endim();
1031 } else if (noim)
1032 endim();
1033 else if (enter_insert_mode && exit_insert_mode) {
1034 vcsync();
1035 goim();
1036 }
1037 (void) vputchar(c);
1038 if((c & QUOTE) == 0)
1039 break;
1040 }
1041
1042 if (!enter_insert_mode || !exit_insert_mode) {
1043 /*
1044 * We are a dumb terminal; brute force update
1045 * the rest of the line; this is very much an n^^2 process,
1046 * and totally unreasonable at low speed.
1047 *
1048 * You asked for it, you get it.
1049 */
1050 int width;
1051 tp = vtube0 + inscol + doomed;
1052 for (i = inscol + doomed; i < tabstart; i++) {
1053 if(*tp != FILLER)
1054 (void) vputchar(*tp);
1055 tp++;
1056 }
1057 hold = oldhold;
1058 vigotoCL(tabstart + inssiz - doomed);
1059 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
1060 (void) vputchar(' ' | QUOTE);
1061 } else {
1062 if (!insert_null_glitch) {
1063 /*
1064 * On terminals without multi-line
1065 * insert in the hardware, we must go fix the segments
1066 * between the inserted text and the following
1067 * tabs, if they are on different lines.
1068 *
1069 * Aaargh.
1070 */
1071 tp = vtube0;
1072 for (j = (inscol + inssiz - 1) / WCOLS + 1;
1073 j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) {
1074 vgotoCL(j * WCOLS);
1075 i = inssiz - doomed;
1076 up = tp + j * WCOLS - i;
1077 goim();
1078 do {
1079 wchar_t wchar;
1080 if((wchar = *up++) != FILLER)
1081 (void) vputchar(wchar);
1082 } while (--i && *up);
1083 }
1084 } else {
1085 /*
1086 * On terminals with multi line inserts,
1087 * life is simpler, just reflect eating of
1088 * the slack.
1089 */
1090 tp = vtube0 + tabend;
1091 for (i = tabsize - (inssiz - doomed); i >= 0; i--) {
1092 if ((*--tp & (QUOTE|TRIM)) == QUOTE) {
1093 --tabslack;
1094 if (tabslack >= slakused)
1095 continue;
1096 }
1097 *tp = ' ' | QUOTE;
1098 }
1099 }
1100 /*
1101 * Blank out the shifted positions to be tab positions.
1102 */
1103 if (shft) {
1104 tp = vtube0 + tabend + shft;
1105 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
1106 if ((*--tp & QUOTE) == 0)
1107 *tp = ' ' | QUOTE;
1108 }
1109 }
1110
1111 /*
1112 * Finally, complete the screen image update
1113 * to reflect the insertion.
1114 */
1115 hold = oldhold;
1116 tp = vtube0 + tabstart; up = tp + inssiz - doomed;
1117 for (i = tabstart; i > inscol + doomed; i--)
1118 *--up = *--tp;
1119 for (i = inssiz; i > 0; i--)
1120 if((c & QUOTE) == 0) {
1121 int width = wcwidth(c);
1122 if (width < 0)
1123 width = 0;
1124 up -= width;
1125 *up++ = c;
1126 if(width)
1127 while(--width)
1128 *up++ = FILLER;
1129 break;
1130 }
1131 else
1132 *--up = c;
1133 doomed = 0;
1134 }
1135
1136 /*
1137 * Go into ``delete mode''. If the
1138 * sequence which goes into delete mode
1139 * is the same as that which goes into insert
1140 * mode, then we are in delete mode already.
1141 */
1142 void
godm(void)1143 godm(void)
1144 {
1145
1146 if (insmode) {
1147 if (eq(enter_delete_mode, enter_insert_mode))
1148 return;
1149 endim();
1150 }
1151 vputp(enter_delete_mode, 0);
1152 }
1153
1154 /*
1155 * If we are coming out of delete mode, but
1156 * delete and insert mode end with the same sequence,
1157 * it wins to pretend we are now in insert mode,
1158 * since we will likely want to be there again soon
1159 * if we just moved over to delete space from part of
1160 * a tab (above).
1161 */
1162 void
enddm(void)1163 enddm(void)
1164 {
1165
1166 if (eq(enter_delete_mode, enter_insert_mode)) {
1167 insmode = 1;
1168 return;
1169 }
1170 vputp(exit_delete_mode, 0);
1171 }
1172
1173 /*
1174 * In and out of insert mode.
1175 * Note that the code here demands that there be
1176 * a string for insert mode (the null string) even
1177 * if the terminal does all insertions a single character
1178 * at a time, since it branches based on whether enter_insert_mode is null.
1179 */
1180 void
goim(void)1181 goim(void)
1182 {
1183
1184 if (!insmode)
1185 vputp(enter_insert_mode, 0);
1186 insmode = 1;
1187 }
1188
1189 void
endim(void)1190 endim(void)
1191 {
1192
1193 if (insmode) {
1194 vputp(exit_insert_mode, 0);
1195 insmode = 0;
1196 }
1197 }
1198
1199 /*
1200 * Put the character c on the screen at the current cursor position.
1201 * This routine handles wraparound and scrolling and understands not
1202 * to roll when splitw is set, i.e. we are working in the echo area.
1203 * There is a bunch of hacking here dealing with the difference between
1204 * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also
1205 * code to deal with terminals which overstrike, including CRT's where
1206 * you can erase overstrikes with some work. CRT's which do underlining
1207 * implicitly which has to be erased (like CONCEPTS) are also handled.
1208 */
1209 int
vputchar(wchar_t c)1210 vputchar(wchar_t c)
1211 {
1212 unsigned char multic[MULTI_BYTE_MAX];
1213 wchar_t *tp;
1214 int d, length, length2, bytelength;
1215 unsigned char *p;
1216 short oldhold = hold;
1217
1218 c &= (QUOTE|TRIM);
1219 #ifdef TRACE
1220 if (trace) {
1221 tracec(c);
1222 }
1223 #endif
1224 if(c & QUOTE)
1225 length = 1;
1226 else
1227 if ((length = wcwidth(c)) < 0)
1228 length = 0;
1229 /* Fix problem of >79 chars on echo line. */
1230 if (destcol >= WCOLS-1 && splitw && destline == WECHO)
1231 pofix();
1232 if (destcol >= WCOLS) {
1233 destline += destcol / WCOLS;
1234 destcol %= WCOLS;
1235 }
1236 if (destline > WBOT && (!splitw || destline > WECHO))
1237 vrollup(destline);
1238 if (destline < 0)
1239 error(gettext("Line too long to fit on screen"));
1240 if(destcol + length - 1 >= WCOLS) {
1241 /* print out split multibyte character using '>' */
1242 hold |= HOLDPUPD;
1243 #ifdef PRESUNEUC
1244 while(length--)
1245 (void) vputchar('>');
1246 #else
1247 if (mc_wrap == 0)
1248 while(length--)
1249 (void) vputchar(mc_filler);
1250 else {
1251 for (length = WCOLS - destcol; length; length--)
1252 (void) vputchar(mc_filler);
1253 hold = oldhold;
1254 if ((length = wcwidth(c)) < 0)
1255 length = 0;
1256 (void) vputchar(c);
1257 }
1258 #endif /* PRESUNEUC */
1259 hold = oldhold;
1260 return (0);
1261 }
1262 tp = vtube[destline] + destcol;
1263 switch (c) {
1264
1265 case '\t':
1266 vgotab();
1267 return (0);
1268
1269 case ' ':
1270 /*
1271 * We can get away without printing a space in a number
1272 * of cases, but not always. We get away with doing nothing
1273 * if we are not in insert mode, and not on a CONCEPT-100
1274 * like terminal, and either not in hardcopy open or in hardcopy
1275 * open on a terminal with no overstriking, provided,
1276 * in all cases, that nothing has ever been displayed
1277 * at this position. Ugh.
1278 */
1279 if (!insmode && !insert_null_glitch && (state != HARDOPEN || over_strike) && (*tp&TRIM) == 0) {
1280 *tp = ' ';
1281 destcol++;
1282 return (0);
1283 }
1284 goto def;
1285
1286 case QUOTE:
1287 if (insmode) {
1288 /*
1289 * When in insert mode, tabs have to expand
1290 * to real, printed blanks.
1291 */
1292 c = ' ' | QUOTE;
1293 goto def;
1294 }
1295 if (*tp == 0) {
1296 /*
1297 * A ``space''.
1298 */
1299 if ((hold & HOLDPUPD) == 0)
1300 *tp = QUOTE;
1301 destcol++;
1302 return (0);
1303 }
1304 /*
1305 * A ``space'' ontop of a part of a tab.
1306 */
1307 if (*tp & QUOTE) {
1308 destcol++;
1309 return (0);
1310 }
1311 c = ' ' | QUOTE;
1312 /* fall into ... */
1313
1314 def:
1315 default:
1316 d = *tp & TRIM;
1317 /*
1318 * Now get away with doing nothing if the characters
1319 * are the same, provided we are not in insert mode
1320 * and if we are in hardopen, that the terminal has overstrike.
1321 */
1322 #ifdef PRESUNEUC
1323 if (rewrite == _OFF && d == (c & TRIM) && !insmode && (state != HARDOPEN || over_strike)) {
1324 #else
1325 if (rewrite == _OFF && d == (c & TRIM) && !insmode &&
1326 (state != HARDOPEN || over_strike) && !multibyte) {
1327 #endif /* PRESUNEUC */
1328 if ((hold & HOLDPUPD) == 0) {
1329 *tp++ = c;
1330 if(length) {
1331 length2 = length;
1332 while(--length2)
1333 *tp++ = FILLER;
1334 }
1335 }
1336 destcol += length;
1337 return (0);
1338 }
1339 /*
1340 * Backwards looking optimization.
1341 * The low level cursor motion routines will use
1342 * a cursor motion right sequence to step 1 character
1343 * right. On, e.g., a DM3025A this is 2 characters
1344 * and printing is noticeably slower at 300 baud.
1345 * Since the low level routines are not allowed to use
1346 * spaces for positioning, we discover the common
1347 * case of a single space here and force a space
1348 * to be printed.
1349 */
1350 if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) {
1351 (void) vputc(' ');
1352 outcol++;
1353 }
1354
1355 /*
1356 * This is an inline expansion a call to vcsync() dictated
1357 * by high frequency in a profile.
1358 */
1359 if (outcol != destcol || outline != destline)
1360 vgoto(destline, destcol);
1361
1362 /*
1363 * Deal with terminals which have overstrike.
1364 * We handle erasing general overstrikes, erasing
1365 * underlines on terminals (such as CONCEPTS) which
1366 * do underlining correctly automatically (e.g. on nroff
1367 * output), and remembering, in hardcopy mode,
1368 * that we have overstruct something.
1369 */
1370 if (!insmode && d && d != ' ' && d != (c & TRIM)) {
1371 if (erase_overstrike && (over_strike || transparent_underline && (c == '_' || d == '_'))) {
1372 (void) vputc(' ');
1373 outcol++, destcol++;
1374 back1();
1375 } else
1376 rubble = 1;
1377 }
1378
1379 /*
1380 * Unless we are just bashing characters around for
1381 * inner working of insert mode, update the display.
1382 */
1383 if ((hold & HOLDPUPD) == 0) {
1384 *tp++ = c;
1385 length2 = length;
1386 /* put in filler characters */
1387 if(length)
1388 while(--length2)
1389 *tp++ = FILLER;
1390
1391 }
1392 /*
1393 * In insert mode, put out the insert_character sequence, padded
1394 * based on the depth of the current line.
1395 * A terminal which had no real insert mode, rather
1396 * opening a character position at a time could do this.
1397 * Actually should use depth to end of current line
1398 * but this rarely matters.
1399 */
1400 if (insmode)
1401 vputp(insert_character, DEPTH(vcline));
1402 c &= TRIM;
1403 bytelength = wctomb((char *)multic, c);
1404 p = multic;
1405 while(bytelength--)
1406 (void) vputc(*p++);
1407
1408 /*
1409 * In insert mode, insert_padding is a post insert pad.
1410 */
1411 if (insmode)
1412 vputp(insert_padding, DEPTH(vcline));
1413 destcol += length;
1414 outcol += length;
1415
1416 /*
1417 * CONCEPT braindamage in early models: after a wraparound
1418 * the next newline is eaten. It's hungry so we just
1419 * feed it now rather than worrying about it.
1420 * Fixed to use return linefeed to work right
1421 * on vt100/tab132 as well as concept.
1422 */
1423 if (eat_newline_glitch && outcol % WCOLS == 0) {
1424 (void) vputc('\r');
1425 (void) vputc('\n');
1426 }
1427 }
1428 return (0);
1429 }
1430
1431 /*
1432 * Delete display positions stcol through endcol.
1433 * Amount of use of special terminal features here is limited.
1434 */
1435 void
1436 physdc(int stcol, int endcol)
1437 {
1438 wchar_t *tp, *up;
1439 wchar_t *tpe;
1440 int i;
1441 int nc = endcol - stcol;
1442
1443 #ifdef IDEBUG
1444 if (trace)
1445 tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol);
1446 #endif
1447 if (!delete_character || nc <= 0)
1448 return;
1449 if (insert_null_glitch) {
1450 /*
1451 * CONCEPT-100 like terminal.
1452 * If there are any ``spaces'' in the material to be
1453 * deleted, then this is too hard, just retype.
1454 */
1455 vprepins();
1456 up = vtube0 + stcol;
1457 i = nc;
1458 do
1459 if ((*up++ & (QUOTE|TRIM)) == QUOTE)
1460 return;
1461 while (--i);
1462 i = 2 * nc;
1463 do
1464 if (*up == 0 || (*up++ & QUOTE) == QUOTE)
1465 return;
1466 while (--i);
1467 vgotoCL(stcol);
1468 } else {
1469 /*
1470 * HP like delete mode.
1471 * Compute how much text we are moving over by deleting.
1472 * If it appears to be faster to just retype
1473 * the line, do nothing and that will be done later.
1474 * We are assuming 2 output characters per deleted
1475 * characters and that clear to end of line is available.
1476 */
1477 i = stcol / WCOLS;
1478 if (i != endcol / WCOLS)
1479 return;
1480 i += LINE(vcline);
1481 stcol %= WCOLS;
1482 endcol %= WCOLS;
1483 up = vtube[i]; tp = up + endcol; tpe = up + WCOLS;
1484 while (tp < tpe && *tp)
1485 tp++;
1486 if (tp - (up + stcol) < 2 * nc)
1487 return;
1488 vgoto(i, stcol);
1489 }
1490
1491 /*
1492 * Go into delete mode and do the actual delete.
1493 * Padding is on delete_character itself.
1494 */
1495 godm();
1496 for (i = nc; i > 0; i--)
1497 vputp(delete_character, DEPTH(vcline));
1498 vputp(exit_delete_mode, 0);
1499
1500 /*
1501 * Straighten up.
1502 * With CONCEPT like terminals, characters are pulled left
1503 * from first following null. HP like terminals shift rest of
1504 * this (single physical) line rigidly.
1505 */
1506 if (insert_null_glitch) {
1507 up = vtube0 + stcol;
1508 tp = vtube0 + endcol;
1509 while (i = *tp++) {
1510 if ((i & (QUOTE|TRIM)) == QUOTE)
1511 break;
1512 *up++ = i;
1513 }
1514 do
1515 *up++ = i;
1516 while (--nc);
1517 } else {
1518 copy(up + stcol, up + endcol, (WCOLS - endcol) * sizeof(wchar_t));
1519 vclrbyte(tpe - nc, nc);
1520 }
1521 }
1522
1523 #ifdef TRACE
1524 tfixnl()
1525 {
1526
1527 if (trubble || techoin)
1528 fprintf(trace, "\n");
1529 trubble = 0, techoin = 0;
1530 }
1531
1532 tvliny()
1533 {
1534 int i;
1535
1536 if (!trace)
1537 return;
1538 tfixnl();
1539 fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline);
1540 for (i = 0; i <= vcnt; i++) {
1541 fprintf(trace, "%d", LINE(i));
1542 if (FLAGS(i) & VDIRT)
1543 fprintf(trace, "*");
1544 if (DEPTH(i) != 1)
1545 fprintf(trace, "<%d>", DEPTH(i));
1546 if (i < vcnt)
1547 fprintf(trace, " ");
1548 }
1549 fprintf(trace, "\n");
1550 }
1551
1552 tracec(c)
1553 int c; /* char --> int */
1554 {
1555
1556 if (!techoin)
1557 trubble = 1;
1558 if (c == ESCAPE)
1559 fprintf(trace, "$");
1560 else if (c & QUOTE) /* for 3B (no sign extension) */
1561 fprintf(trace, "~%c", ctlof(c&TRIM));
1562 else if (c < ' ' || c == DELETE)
1563 fprintf(trace, "^%c", ctlof(c));
1564 else
1565 fprintf(trace, "%c", c);
1566 }
1567 #endif
1568
1569 /*
1570 * Put a character with possible tracing.
1571 */
1572 int
1573 vputch(char c)
1574 {
1575
1576 #ifdef TRACE
1577 if (trace) {
1578 tracec(c);
1579 }
1580 #endif
1581 (void) vputc(c);
1582 return (0);
1583 }
1584