1 /* $NetBSD: cr_put.c,v 1.35 2021/06/27 23:57:08 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 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)cr_put.c 8.3 (Berkeley) 5/4/94"; 36 #else 37 __RCSID("$NetBSD: cr_put.c,v 1.35 2021/06/27 23:57:08 blymn Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <string.h> 42 43 #include "curses.h" 44 #include "curses_private.h" 45 46 #define HARDTABS 8 47 48 /* 49 * Terminal driving and line formatting routines. Basic motion optimizations 50 * are done here as well as formatting lines (printing of control characters, 51 * line numbering and the like). 52 */ 53 54 /* Stub function for the users. */ 55 int 56 mvcur(int ly, int lx, int y, int x) 57 { 58 return (__mvcur(ly, lx, y, x, 0)); 59 } 60 61 static void fgoto(int); 62 static int plod(int, int); 63 static int plodput(int); 64 static int tabcol(int, int); 65 66 static int outcol, outline, destcol, destline; 67 68 /* 69 * Sync the position of the output cursor. Most work here is rounding for 70 * terminal boundaries getting the column position implied by wraparound or 71 * the lack thereof and rolling up the screen to get destline on the screen. 72 */ 73 int 74 __mvcur(int ly, int lx, int y, int x, int in_refresh) 75 { 76 #ifdef DEBUG 77 __CTRACE(__CTRACE_OUTPUT, 78 "mvcur: moving cursor from (%d, %d) to (%d, %d) in refresh %d\n", ly, lx, y, x, in_refresh); 79 #endif 80 destcol = x; 81 destline = y; 82 outcol = lx; 83 outline = ly; 84 fgoto(in_refresh); 85 return (OK); 86 } 87 88 static void 89 fgoto(int in_refresh) 90 { 91 int c, l; 92 char *cgp; 93 94 #ifdef DEBUG 95 __CTRACE(__CTRACE_OUTPUT, "fgoto: in_refresh=%d\n", in_refresh); 96 __CTRACE(__CTRACE_OUTPUT, "fgoto: outcol=%d, outline=%d, destcol=%d, destline=%d\n", 97 outcol, outline, destcol, destline); 98 #endif /* DEBUG */ 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 recieving 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 #ifdef DEBUG 176 __CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp); 177 #endif /* DEBUG */ 178 if (outcol != COLS - 1 && 179 plod((int) strlen(cgp), in_refresh) > 0) 180 plod(0, in_refresh); 181 else 182 tputs(cgp, 0, __cputchar); 183 } else 184 plod(0, in_refresh); 185 outline = destline; 186 outcol = destcol; 187 } 188 189 /* 190 * Move (slowly) to destination. 191 * Hard thing here is using home cursor on really deficient terminals. 192 * Otherwise just use cursor motions, hacking use of tabs and overtabbing 193 * and backspace. 194 * 195 * XXX this needs to be revisited for wide characters since we may output 196 * XXX more than one byte for a character. 197 */ 198 199 static int plodcnt, plodflg; 200 201 static int 202 plodput(int c) 203 { 204 if (plodflg) 205 --plodcnt; 206 else 207 __cputchar(c); 208 return 0; 209 } 210 211 static int 212 plod(int cnt, int in_refresh) 213 { 214 int i, j, k, soutcol, soutline; 215 216 #ifdef DEBUG 217 __CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n", 218 cnt, in_refresh); 219 __CTRACE(__CTRACE_OUTPUT, "plod: plodding from col %d, row %d to col %d, row %d\n", 220 outcol, outline, destcol, destline); 221 #endif /* DEBUG */ 222 plodcnt = plodflg = cnt; 223 soutcol = outcol; 224 soutline = outline; 225 226 /* 227 * Consider homing and moving down/right from there, vs. moving 228 * directly with local motions to the right spot. 229 */ 230 if (cursor_home) { 231 /* 232 * i is the cost to home and tab/space to the right to get to 233 * the proper column. This assumes nd space costs 1 char. So 234 * i + destcol is cost of motion with home. 235 */ 236 if (__GT) 237 i = (destcol / HARDTABS) + (destcol % HARDTABS); 238 else 239 i = destcol; 240 241 /* j is cost to move locally without homing. */ 242 if (destcol >= outcol) { /* if motion is to the right */ 243 j = destcol / HARDTABS - outcol / HARDTABS; 244 if (__GT && j) 245 j += destcol % HARDTABS; 246 else 247 j = destcol - outcol; 248 } else 249 /* leftward motion only works if we can backspace. */ 250 if (outcol - destcol <= i) 251 /* Cheaper to backspace. */ 252 i = j = outcol - destcol; 253 else 254 /* Impossibly expensive. */ 255 j = i + 1; 256 257 /* k is the absolute value of vertical distance. */ 258 k = outline - destline; 259 if (k < 0) 260 k = -k; 261 j += k; 262 263 /* Decision. We may not have a choice if no up. */ 264 if (i + destline < j || (!cursor_up && destline < outline)) { 265 /* 266 * Cheaper to home. Do it now and pretend it's a 267 * regular local motion. 268 */ 269 tputs(cursor_home, 0, plodput); 270 outcol = outline = 0; 271 } else 272 if (cursor_to_ll) { 273 /* 274 * Quickly consider homing down and moving from 275 * there. Assume cost of ll is 2. 276 */ 277 k = (LINES - 1) - destline; 278 if (i + k + 2 < j && (k <= 0 || cursor_up)) { 279 tputs(cursor_to_ll, 0, plodput); 280 outcol = 0; 281 outline = LINES - 1; 282 } 283 } 284 } else 285 /* No home and no up means it's impossible. */ 286 if (!cursor_up && destline < outline) 287 return (-1); 288 if (__GT) 289 i = destcol % HARDTABS + destcol / HARDTABS; 290 else 291 i = destcol; 292 #ifdef notdef 293 if (back_tab && outcol > destcol && 294 (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) { 295 j *= (k = strlen(back_tab)); 296 if ((k += (destcol & 7)) > 4) 297 j += 8 - (destcol & 7); 298 else 299 j += k; 300 } else 301 #endif 302 j = outcol - destcol; 303 304 /* 305 * If we will later need a \n which will turn into a \r\n by the 306 * system or the terminal, then don't bother to try to \r. 307 */ 308 if ((__NONL || !__pfast) && outline < destline) 309 goto dontcr; 310 311 /* 312 * If the terminal will do a \r\n and there isn't room for it, then 313 * we can't afford a \r. 314 */ 315 if (!carriage_return && outline >= destline) 316 goto dontcr; 317 318 /* 319 * If it will be cheaper, or if we can't back up, then send a return 320 * preliminarily. 321 */ 322 if (j > i + 1 || outcol > destcol) { 323 /* 324 * BUG: this doesn't take the (possibly long) length of cr 325 * into account. 326 */ 327 if (carriage_return) 328 tputs(carriage_return, 0, plodput); 329 else 330 plodput('\r'); 331 if (!carriage_return) { 332 if (cursor_down) 333 tputs(cursor_down, 0, plodput); 334 else 335 plodput('\n'); 336 outline++; 337 } 338 339 outcol = 0; 340 } 341 dontcr:while (outline < destline) { 342 outline++; 343 if (cursor_down) 344 tputs(cursor_down, 0, plodput); 345 else 346 plodput('\n'); 347 if (plodcnt < 0) 348 goto out; 349 /* 350 * If the terminal does a CR with NL or we are in 351 * a mode where a \n will result in an implicit \r 352 * then adjust the outcol to match iff we actually 353 * emitted said \n. 354 */ 355 if ((__NONL || __pfast == 0) && 356 (!cursor_down || (*cursor_down == '\n'))) 357 outcol = 0; 358 } 359 #ifdef notdef 360 if (back_tab) 361 k = (int) strlen(back_tab); 362 #endif 363 while (outcol > destcol) { 364 if (plodcnt < 0) 365 goto out; 366 #ifdef notdef 367 if (back_tab && outcol - destcol > k + 4) { 368 tputs(back_tab, 0, plodput); 369 outcol--; 370 outcol &= ~7; 371 continue; 372 } 373 #endif 374 outcol--; 375 if (cursor_left) 376 tputs(cursor_left, 0, plodput); 377 else 378 plodput('\b'); 379 } 380 while (outline > destline) { 381 outline--; 382 tputs(cursor_up, 0, plodput); 383 if (plodcnt < 0) 384 goto out; 385 } 386 if (__GT && destcol - outcol > 1) { 387 for (;;) { 388 i = tabcol(outcol, HARDTABS); 389 if (i > destcol) 390 break; 391 if (tab) 392 tputs(tab, 0, plodput); 393 else 394 plodput('\t'); 395 outcol = i; 396 } 397 if (destcol - outcol > 4 && i < COLS) { 398 if (tab) 399 tputs(tab, 0, plodput); 400 else 401 plodput('\t'); 402 outcol = i; 403 while (outcol > destcol) { 404 outcol--; 405 if (cursor_left) 406 tputs(cursor_left, 0, plodput); 407 else 408 plodput('\b'); 409 } 410 } 411 } 412 while (outcol < destcol) { 413 /* 414 * Move one char to the right. We don't use nd space because 415 * it's better to just print the char we are moving over. 416 */ 417 if (in_refresh) 418 if (plodflg) /* Avoid a complex calculation. */ 419 plodcnt--; 420 else { 421 #ifndef HAVE_WCHAR 422 i = curscr->alines[outline]->line[outcol].ch 423 & __CHARTEXT; 424 if (curscr->alines[outline]->line[outcol].attr 425 == curscr->wattr) 426 __cputchar(i); 427 #else 428 if ((curscr->alines[outline]->line[outcol].attr 429 & WA_ATTRIBUTES) 430 == curscr->wattr) { 431 switch (WCOL(curscr->alines[outline]->line[outcol])) { 432 case 1: 433 __cputwchar(curscr->alines[outline]->line[outcol].ch); 434 __cursesi_putnsp(curscr->alines[outline]->line[outcol].nsp, 435 outline, 436 outcol); 437 #ifdef DEBUG 438 __CTRACE(__CTRACE_OUTPUT, 439 "plod: (%d,%d)WCOL(%d), " 440 "putwchar(%x)\n", 441 outline, outcol, 442 WCOL(curscr->alines[outline]->line[outcol]), 443 curscr->alines[outline]->line[outcol].ch); 444 #endif /* DEBUG */ 445 /*FALLTHROUGH*/ 446 case 0: 447 break; 448 default: 449 goto nondes; 450 } 451 } 452 #endif /* HAVE_WCHAR */ 453 else 454 goto nondes; 455 } 456 else 457 nondes: if (cursor_right) 458 tputs(cursor_right, 0, plodput); 459 else 460 plodput(' '); 461 outcol++; 462 if (plodcnt < 0) 463 goto out; 464 } 465 466 out: if (plodflg) { 467 outcol = soutcol; 468 outline = soutline; 469 } 470 #ifdef DEBUG 471 __CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt); 472 #endif /* DEBUG */ 473 return plodcnt; 474 } 475 476 /* 477 * Return the column number that results from being in column col and 478 * hitting a tab, where tabs are set every ts columns. Work right for 479 * the case where col > COLS, even if ts does not divide COLS. 480 */ 481 static int 482 tabcol(int col, int ts) 483 { 484 int offset; 485 486 if (col >= COLS) { 487 offset = COLS * (col / COLS); 488 col -= offset; 489 } else 490 offset = 0; 491 return (col + ts - (col % ts) + offset); 492 } 493