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 37static 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. */ 57int 58mvcur(int ly, int lx, int y, int x) 59{ 60 return (__mvcur(ly, lx, y, x, 0)); 61} 62 63static void fgoto(int); 64static int plod(int, int); 65static int plodput(int); 66static int tabcol(int, int); 67 68static 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 */ 75int 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 89static void 90fgoto(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 195static int plodcnt, plodflg; 196#ifdef HAVE_WCHAR 197static char s[MB_LEN_MAX]; 198#endif 199 200static int 201plodput(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 220static int 221plod(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 } 350dontcr: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 493out: 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 */ 506static int 507tabcol(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