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 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 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 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 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 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 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