1 /* $NetBSD: cr_put.c,v 1.40 2022/10/19 06:09:27 blymn Exp $ */
2
3 /*
4 * Copyright (c) 1981, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #include <limits.h>
34 #include <stdlib.h>
35 #ifndef lint
36 #if 0
37 static char sccsid[] = "@(#)cr_put.c 8.3 (Berkeley) 5/4/94";
38 #else
39 __RCSID("$NetBSD: cr_put.c,v 1.40 2022/10/19 06:09:27 blymn Exp $");
40 #endif
41 #endif /* not lint */
42
43 #include <string.h>
44
45 #include "curses.h"
46 #include "curses_private.h"
47
48 #define HARDTABS 8
49
50 /*
51 * Terminal driving and line formatting routines. Basic motion optimizations
52 * are done here as well as formatting lines (printing of control characters,
53 * line numbering and the like).
54 */
55
56 /* Stub function for the users. */
57 int
mvcur(int ly,int lx,int y,int x)58 mvcur(int ly, int lx, int y, int x)
59 {
60 return (__mvcur(ly, lx, y, x, 0));
61 }
62
63 static void fgoto(int);
64 static int plod(int, int);
65 static int plodput(int);
66 static int tabcol(int, int);
67
68 static int outcol, outline, destcol, destline;
69
70 /*
71 * Sync the position of the output cursor. Most work here is rounding for
72 * terminal boundaries getting the column position implied by wraparound or
73 * the lack thereof and rolling up the screen to get destline on the screen.
74 */
75 int
__mvcur(int ly,int lx,int y,int x,int in_refresh)76 __mvcur(int ly, int lx, int y, int x, int in_refresh)
77 {
78 __CTRACE(__CTRACE_OUTPUT,
79 "mvcur: moving cursor from (%d, %d) to (%d, %d) in refresh %d\n",
80 ly, lx, y, x, in_refresh);
81 destcol = x;
82 destline = y;
83 outcol = lx;
84 outline = ly;
85 fgoto(in_refresh);
86 return (OK);
87 }
88
89 static void
fgoto(int in_refresh)90 fgoto(int in_refresh)
91 {
92 int c, l;
93 char *cgp;
94
95 __CTRACE(__CTRACE_OUTPUT, "fgoto: in_refresh=%d\n", in_refresh);
96 __CTRACE(__CTRACE_OUTPUT,
97 "fgoto: outcol=%d, outline=%d, destcol=%d, destline=%d\n",
98 outcol, outline, destcol, destline);
99 if (destcol >= COLS) {
100 destline += destcol / COLS;
101 destcol %= COLS;
102 }
103 if (outcol >= COLS) {
104 l = (outcol + 1) / COLS;
105 outline += l;
106 outcol %= COLS;
107 if (auto_left_margin == 0) {
108 while (l > 0) {
109 if (__pfast) {
110 if (carriage_return)
111 tputs(carriage_return,
112 0, __cputchar);
113 else
114 __cputchar('\r');
115 }
116 if (cursor_down)
117 tputs(cursor_down, 0, __cputchar);
118 else
119 __cputchar('\n');
120 l--;
121 }
122 outcol = 0;
123 }
124 if (outline > LINES - 1) {
125 destline -= outline - (LINES - 1);
126 outline = LINES - 1;
127 }
128 }
129 if (destline >= LINES) {
130 l = destline;
131 destline = LINES - 1;
132 if (outline < LINES - 1) {
133 c = destcol;
134 if (__pfast == 0 && !cursor_address)
135 destcol = 0;
136 fgoto(in_refresh);
137 destcol = c;
138 }
139 while (l >= LINES) {
140 /* The following linefeed (or simulation thereof) is
141 * supposed to scroll up the screen, since we are on
142 * the bottom line. We make the assumption that
143 * linefeed will scroll. If ns is in the capability
144 * list this won't work. We should probably have an
145 * sc capability but sf will generally take the place
146 * if it works.
147 *
148 * Superbee glitch: in the middle of the screen have to
149 * use esc B (down) because linefeed screws up in
150 * "Efficient Paging" (what a joke) mode (which is
151 * essential in some SB's because CRLF mode puts
152 * garbage in at end of memory), but you must use
153 * linefeed to scroll since down arrow won't go past
154 * memory end. I turned this off after receiving Paul
155 * Eggert's Superbee description which wins better. */
156 if (cursor_down /* && !__tc_xb */ && __pfast)
157 tputs(cursor_down, 0, __cputchar);
158 else
159 __cputchar('\n');
160 l--;
161 if (__pfast == 0)
162 outcol = 0;
163 }
164 }
165 if (destline < outline && !(cursor_address || cursor_up))
166 destline = outline;
167
168 if (cursor_address &&
169 (cgp = tiparm(cursor_address, destline, destcol)))
170 {
171 /*
172 * Need this condition due to inconsistent behavior
173 * of backspace on the last column.
174 */
175 __CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp);
176 if (outcol != COLS - 1 &&
177 plod((int) strlen(cgp), in_refresh) > 0)
178 plod(0, in_refresh);
179 else
180 tputs(cgp, 0, __cputchar);
181 } else
182 plod(0, in_refresh);
183 outline = destline;
184 outcol = destcol;
185 }
186
187 /*
188 * Move (slowly) to destination.
189 * Hard thing here is using home cursor on really deficient terminals.
190 * Otherwise just use cursor motions, hacking use of tabs and overtabbing
191 * and backspace.
192 *
193 */
194
195 static int plodcnt, plodflg;
196 #ifdef HAVE_WCHAR
197 static char s[MB_LEN_MAX];
198 #endif
199
200 static int
plodput(int c)201 plodput(int c)
202 {
203 if (plodflg) {
204 int cw;
205
206 #ifdef HAVE_WCHAR
207 cw = wctomb(s, c);
208 if (cw < 0)
209 cw = 1;
210 #else
211 cw = 1;
212 #endif /* HAVE_WCHAR */
213
214 plodcnt -= cw;
215 } else
216 __cputchar(c);
217 return 0;
218 }
219
220 static int
plod(int cnt,int in_refresh)221 plod(int cnt, int in_refresh)
222 {
223 int i, j, k, soutcol, soutline;
224 __LDATA *csp;
225
226 __CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n",
227 cnt, in_refresh);
228 __CTRACE(__CTRACE_OUTPUT,
229 "plod: plodding from col %d, row %d to col %d, row %d\n",
230 outcol, outline, destcol, destline);
231 plodcnt = plodflg = cnt;
232 soutcol = outcol;
233 soutline = outline;
234
235 /*
236 * Consider homing and moving down/right from there, vs. moving
237 * directly with local motions to the right spot.
238 */
239 if (cursor_home) {
240 /*
241 * i is the cost to home and tab/space to the right to get to
242 * the proper column. This assumes nd space costs 1 char. So
243 * i + destcol is cost of motion with home.
244 */
245 if (__GT)
246 i = (destcol / HARDTABS) + (destcol % HARDTABS);
247 else
248 i = destcol;
249
250 /* j is cost to move locally without homing. */
251 if (destcol >= outcol) { /* if motion is to the right */
252 j = destcol / HARDTABS - outcol / HARDTABS;
253 if (__GT && j)
254 j += destcol % HARDTABS;
255 else
256 j = destcol - outcol;
257 } else
258 /* leftward motion only works if we can backspace. */
259 if (outcol - destcol <= i)
260 /* Cheaper to backspace. */
261 i = j = outcol - destcol;
262 else
263 /* Impossibly expensive. */
264 j = i + 1;
265
266 /* k is the absolute value of vertical distance. */
267 k = outline - destline;
268 if (k < 0)
269 k = -k;
270 j += k;
271
272 /* Decision. We may not have a choice if no up. */
273 if (i + destline < j || (!cursor_up && destline < outline)) {
274 /*
275 * Cheaper to home. Do it now and pretend it's a
276 * regular local motion.
277 */
278 tputs(cursor_home, 0, plodput);
279 outcol = outline = 0;
280 } else
281 if (cursor_to_ll) {
282 /*
283 * Quickly consider homing down and moving from
284 * there. Assume cost of ll is 2.
285 */
286 k = (LINES - 1) - destline;
287 if (i + k + 2 < j && (k <= 0 || cursor_up)) {
288 tputs(cursor_to_ll, 0, plodput);
289 outcol = 0;
290 outline = LINES - 1;
291 }
292 }
293 } else
294 /* No home and no up means it's impossible. */
295 if (!cursor_up && destline < outline)
296 return (-1);
297 if (__GT)
298 i = destcol % HARDTABS + destcol / HARDTABS;
299 else
300 i = destcol;
301 #ifdef notdef
302 if (back_tab && outcol > destcol &&
303 (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) {
304 j *= (k = strlen(back_tab));
305 if ((k += (destcol & 7)) > 4)
306 j += 8 - (destcol & 7);
307 else
308 j += k;
309 } else
310 #endif
311 j = outcol - destcol;
312
313 /*
314 * If we will later need a \n which will turn into a \r\n by the
315 * system or the terminal, then don't bother to try to \r.
316 */
317 if ((__NONL || !__pfast) && outline < destline)
318 goto dontcr;
319
320 /*
321 * If the terminal will do a \r\n and there isn't room for it, then
322 * we can't afford a \r.
323 */
324 if (!carriage_return && outline >= destline)
325 goto dontcr;
326
327 /*
328 * If it will be cheaper, or if we can't back up, then send a return
329 * preliminarily.
330 */
331 if (j > i + 1 || outcol > destcol) {
332 /*
333 * BUG: this doesn't take the (possibly long) length of cr
334 * into account.
335 */
336 if (carriage_return)
337 tputs(carriage_return, 0, plodput);
338 else
339 plodput('\r');
340 if (!carriage_return) {
341 if (cursor_down)
342 tputs(cursor_down, 0, plodput);
343 else
344 plodput('\n');
345 outline++;
346 }
347
348 outcol = 0;
349 }
350 dontcr:while (outline < destline) {
351 outline++;
352 if (cursor_down)
353 tputs(cursor_down, 0, plodput);
354 else
355 plodput('\n');
356 if (plodcnt < 0)
357 goto out;
358 /*
359 * If the terminal does a CR with NL or we are in
360 * a mode where a \n will result in an implicit \r
361 * then adjust the outcol to match iff we actually
362 * emitted said \n.
363 */
364 if ((__NONL || __pfast == 0) &&
365 (!cursor_down || (*cursor_down == '\n')))
366 outcol = 0;
367 }
368 #ifdef notdef
369 if (back_tab)
370 k = (int) strlen(back_tab);
371 #endif
372 while (outcol > destcol) {
373 if (plodcnt < 0)
374 goto out;
375 #ifdef notdef
376 if (back_tab && outcol - destcol > k + 4) {
377 tputs(back_tab, 0, plodput);
378 outcol--;
379 outcol &= ~7;
380 continue;
381 }
382 #endif
383 outcol--;
384 if (cursor_left)
385 tputs(cursor_left, 0, plodput);
386 else
387 plodput('\b');
388 }
389 while (outline > destline) {
390 outline--;
391 tputs(cursor_up, 0, plodput);
392 if (plodcnt < 0)
393 goto out;
394 }
395 if (__GT && destcol - outcol > 1) {
396 for (;;) {
397 i = tabcol(outcol, HARDTABS);
398 if (i > destcol)
399 break;
400 if (tab)
401 tputs(tab, 0, plodput);
402 else
403 plodput('\t');
404 outcol = i;
405 }
406 if (destcol - outcol > 4 && i < COLS) {
407 if (tab)
408 tputs(tab, 0, plodput);
409 else
410 plodput('\t');
411 outcol = i;
412 while (outcol > destcol) {
413 outcol--;
414 if (cursor_left)
415 tputs(cursor_left, 0, plodput);
416 else
417 plodput('\b');
418 }
419 }
420 }
421
422 #ifdef HAVE_WCHAR
423 /*
424 * If destcol is halfway through a multicolumn
425 * wide char, we have no chance of plodding.
426 */
427 if (curscr->alines[outline]->line[outcol].cflags & CA_CONTINUATION) {
428 plodcnt = -1;
429 goto out;
430 }
431 #endif /* HAVE_WCHAR */
432
433 while (outcol < destcol) {
434 int chw;
435
436 csp = &curscr->alines[outline]->line[outcol];
437 #ifdef HAVE_WCHAR
438 chw = csp->wcols;
439 #else
440 chw = 1;
441 #endif /* HAVE_WCHAR */
442
443
444 /*
445 * Move one char to the right. We don't use nd space because
446 * it's better to just print the char we are moving over.
447 */
448 if (in_refresh)
449 if (plodflg) /* Avoid a complex calculation. */
450 plodcnt--;
451 else {
452 #ifndef HAVE_WCHAR
453 i = csp->ch & __CHARTEXT;
454 if (csp->attr == curscr->wattr)
455 __cputchar(i);
456 #else
457 if ((csp->attr & WA_ATTRIBUTES)
458 == curscr->wattr) {
459 if (csp->cflags & CA_CONTINUATION)
460 goto nondes;
461
462 if (csp->wcols >= 1) {
463 __cputwchar(csp->ch);
464 __cursesi_putnsp(csp->nsp,
465 outline,
466 outcol);
467 __CTRACE(__CTRACE_OUTPUT,
468 "plod: (%d,%d)wcols(%d), "
469 "putwchar(%x)\n",
470 outline, outcol,
471 csp->wcols, csp->ch);
472 }
473
474 if (csp->wcols == 0)
475 break;
476 }
477 #endif /* HAVE_WCHAR */
478 else
479 goto nondes;
480 }
481 else {
482 nondes: if (cursor_right)
483 tputs(cursor_right, 0, plodput);
484 else
485 plodput(' ');
486 }
487
488 outcol += chw;
489 if (plodcnt < 0)
490 goto out;
491 }
492
493 out: if (plodflg) {
494 outcol = soutcol;
495 outline = soutline;
496 }
497 __CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt);
498 return plodcnt;
499 }
500
501 /*
502 * Return the column number that results from being in column col and
503 * hitting a tab, where tabs are set every ts columns. Work right for
504 * the case where col > COLS, even if ts does not divide COLS.
505 */
506 static int
tabcol(int col,int ts)507 tabcol(int col, int ts)
508 {
509 int offset;
510
511 if (col >= COLS) {
512 offset = COLS * (col / COLS);
513 col -= offset;
514 } else
515 offset = 0;
516 return (col + ts - (col % ts) + offset);
517 }
518