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