1 /* $NetBSD: cr_put.c,v 1.39 2022/04/10 09:50:44 andvar 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.39 2022/04/10 09:50:44 andvar 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 225 __CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n", 226 cnt, in_refresh); 227 __CTRACE(__CTRACE_OUTPUT, 228 "plod: plodding from col %d, row %d to col %d, row %d\n", 229 outcol, outline, destcol, destline); 230 plodcnt = plodflg = cnt; 231 soutcol = outcol; 232 soutline = outline; 233 234 /* 235 * Consider homing and moving down/right from there, vs. moving 236 * directly with local motions to the right spot. 237 */ 238 if (cursor_home) { 239 /* 240 * i is the cost to home and tab/space to the right to get to 241 * the proper column. This assumes nd space costs 1 char. So 242 * i + destcol is cost of motion with home. 243 */ 244 if (__GT) 245 i = (destcol / HARDTABS) + (destcol % HARDTABS); 246 else 247 i = destcol; 248 249 /* j is cost to move locally without homing. */ 250 if (destcol >= outcol) { /* if motion is to the right */ 251 j = destcol / HARDTABS - outcol / HARDTABS; 252 if (__GT && j) 253 j += destcol % HARDTABS; 254 else 255 j = destcol - outcol; 256 } else 257 /* leftward motion only works if we can backspace. */ 258 if (outcol - destcol <= i) 259 /* Cheaper to backspace. */ 260 i = j = outcol - destcol; 261 else 262 /* Impossibly expensive. */ 263 j = i + 1; 264 265 /* k is the absolute value of vertical distance. */ 266 k = outline - destline; 267 if (k < 0) 268 k = -k; 269 j += k; 270 271 /* Decision. We may not have a choice if no up. */ 272 if (i + destline < j || (!cursor_up && destline < outline)) { 273 /* 274 * Cheaper to home. Do it now and pretend it's a 275 * regular local motion. 276 */ 277 tputs(cursor_home, 0, plodput); 278 outcol = outline = 0; 279 } else 280 if (cursor_to_ll) { 281 /* 282 * Quickly consider homing down and moving from 283 * there. Assume cost of ll is 2. 284 */ 285 k = (LINES - 1) - destline; 286 if (i + k + 2 < j && (k <= 0 || cursor_up)) { 287 tputs(cursor_to_ll, 0, plodput); 288 outcol = 0; 289 outline = LINES - 1; 290 } 291 } 292 } else 293 /* No home and no up means it's impossible. */ 294 if (!cursor_up && destline < outline) 295 return (-1); 296 if (__GT) 297 i = destcol % HARDTABS + destcol / HARDTABS; 298 else 299 i = destcol; 300 #ifdef notdef 301 if (back_tab && outcol > destcol && 302 (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) { 303 j *= (k = strlen(back_tab)); 304 if ((k += (destcol & 7)) > 4) 305 j += 8 - (destcol & 7); 306 else 307 j += k; 308 } else 309 #endif 310 j = outcol - destcol; 311 312 /* 313 * If we will later need a \n which will turn into a \r\n by the 314 * system or the terminal, then don't bother to try to \r. 315 */ 316 if ((__NONL || !__pfast) && outline < destline) 317 goto dontcr; 318 319 /* 320 * If the terminal will do a \r\n and there isn't room for it, then 321 * we can't afford a \r. 322 */ 323 if (!carriage_return && outline >= destline) 324 goto dontcr; 325 326 /* 327 * If it will be cheaper, or if we can't back up, then send a return 328 * preliminarily. 329 */ 330 if (j > i + 1 || outcol > destcol) { 331 /* 332 * BUG: this doesn't take the (possibly long) length of cr 333 * into account. 334 */ 335 if (carriage_return) 336 tputs(carriage_return, 0, plodput); 337 else 338 plodput('\r'); 339 if (!carriage_return) { 340 if (cursor_down) 341 tputs(cursor_down, 0, plodput); 342 else 343 plodput('\n'); 344 outline++; 345 } 346 347 outcol = 0; 348 } 349 dontcr:while (outline < destline) { 350 outline++; 351 if (cursor_down) 352 tputs(cursor_down, 0, plodput); 353 else 354 plodput('\n'); 355 if (plodcnt < 0) 356 goto out; 357 /* 358 * If the terminal does a CR with NL or we are in 359 * a mode where a \n will result in an implicit \r 360 * then adjust the outcol to match iff we actually 361 * emitted said \n. 362 */ 363 if ((__NONL || __pfast == 0) && 364 (!cursor_down || (*cursor_down == '\n'))) 365 outcol = 0; 366 } 367 #ifdef notdef 368 if (back_tab) 369 k = (int) strlen(back_tab); 370 #endif 371 while (outcol > destcol) { 372 if (plodcnt < 0) 373 goto out; 374 #ifdef notdef 375 if (back_tab && outcol - destcol > k + 4) { 376 tputs(back_tab, 0, plodput); 377 outcol--; 378 outcol &= ~7; 379 continue; 380 } 381 #endif 382 outcol--; 383 if (cursor_left) 384 tputs(cursor_left, 0, plodput); 385 else 386 plodput('\b'); 387 } 388 while (outline > destline) { 389 outline--; 390 tputs(cursor_up, 0, plodput); 391 if (plodcnt < 0) 392 goto out; 393 } 394 if (__GT && destcol - outcol > 1) { 395 for (;;) { 396 i = tabcol(outcol, HARDTABS); 397 if (i > destcol) 398 break; 399 if (tab) 400 tputs(tab, 0, plodput); 401 else 402 plodput('\t'); 403 outcol = i; 404 } 405 if (destcol - outcol > 4 && i < COLS) { 406 if (tab) 407 tputs(tab, 0, plodput); 408 else 409 plodput('\t'); 410 outcol = i; 411 while (outcol > destcol) { 412 outcol--; 413 if (cursor_left) 414 tputs(cursor_left, 0, plodput); 415 else 416 plodput('\b'); 417 } 418 } 419 } 420 421 #ifdef HAVE_WCHAR 422 /* 423 * If destcol is halfway through a multicolumn 424 * wide char, we have no chance of plodding. 425 */ 426 k = outcol - destcol; 427 if (k < 0) 428 k = -k; 429 if ((k != 0) && (curscr->alines[outline]->line[outcol].wcols > k)) { 430 plodcnt = -1; 431 goto out; 432 } 433 #endif /* HAVE_WCHAR */ 434 435 while (outcol < destcol) { 436 /* 437 * Move one char to the right. We don't use nd space because 438 * it's better to just print the char we are moving over. 439 */ 440 if (in_refresh) 441 if (plodflg) /* Avoid a complex calculation. */ 442 plodcnt--; 443 else { 444 #ifndef HAVE_WCHAR 445 i = curscr->alines[outline]->line[outcol].ch 446 & __CHARTEXT; 447 if (curscr->alines[outline]->line[outcol].attr 448 == curscr->wattr) 449 __cputchar(i); 450 #else 451 if ((curscr->alines[outline]->line[outcol].attr 452 & WA_ATTRIBUTES) 453 == curscr->wattr) { 454 switch (curscr->alines[outline]->line[outcol].wcols) { 455 case 1: 456 __cputwchar(curscr->alines[outline]->line[outcol].ch); 457 __cursesi_putnsp(curscr->alines[outline]->line[outcol].nsp, 458 outline, 459 outcol); 460 __CTRACE(__CTRACE_OUTPUT, 461 "plod: (%d,%d)wcols(%d), " 462 "putwchar(%x)\n", 463 outline, outcol, 464 curscr->alines[outline]->line[outcol].wcols, 465 curscr->alines[outline]->line[outcol].ch); 466 /*FALLTHROUGH*/ 467 case 0: 468 break; 469 default: 470 goto nondes; 471 } 472 } 473 #endif /* HAVE_WCHAR */ 474 else 475 goto nondes; 476 } 477 else 478 nondes: if (cursor_right) 479 tputs(cursor_right, 0, plodput); 480 else 481 plodput(' '); 482 outcol++; 483 if (plodcnt < 0) 484 goto out; 485 } 486 487 out: if (plodflg) { 488 outcol = soutcol; 489 outline = soutline; 490 } 491 __CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt); 492 return plodcnt; 493 } 494 495 /* 496 * Return the column number that results from being in column col and 497 * hitting a tab, where tabs are set every ts columns. Work right for 498 * the case where col > COLS, even if ts does not divide COLS. 499 */ 500 static int 501 tabcol(int col, int ts) 502 { 503 int offset; 504 505 if (col >= COLS) { 506 offset = COLS * (col / COLS); 507 col -= offset; 508 } else 509 offset = 0; 510 return (col + ts - (col % ts) + offset); 511 } 512