1 /* $NetBSD: cr_put.c,v 1.37 2021/09/06 07:45:48 rin 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.37 2021/09/06 07:45:48 rin 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 __CTRACE(__CTRACE_OUTPUT, 77 "mvcur: moving cursor from (%d, %d) to (%d, %d) in refresh %d\n", 78 ly, lx, y, x, in_refresh); 79 destcol = x; 80 destline = y; 81 outcol = lx; 82 outline = ly; 83 fgoto(in_refresh); 84 return (OK); 85 } 86 87 static void 88 fgoto(int in_refresh) 89 { 90 int c, l; 91 char *cgp; 92 93 __CTRACE(__CTRACE_OUTPUT, "fgoto: in_refresh=%d\n", in_refresh); 94 __CTRACE(__CTRACE_OUTPUT, 95 "fgoto: outcol=%d, outline=%d, destcol=%d, destline=%d\n", 96 outcol, outline, destcol, destline); 97 if (destcol >= COLS) { 98 destline += destcol / COLS; 99 destcol %= COLS; 100 } 101 if (outcol >= COLS) { 102 l = (outcol + 1) / COLS; 103 outline += l; 104 outcol %= COLS; 105 if (auto_left_margin == 0) { 106 while (l > 0) { 107 if (__pfast) { 108 if (carriage_return) 109 tputs(carriage_return, 110 0, __cputchar); 111 else 112 __cputchar('\r'); 113 } 114 if (cursor_down) 115 tputs(cursor_down, 0, __cputchar); 116 else 117 __cputchar('\n'); 118 l--; 119 } 120 outcol = 0; 121 } 122 if (outline > LINES - 1) { 123 destline -= outline - (LINES - 1); 124 outline = LINES - 1; 125 } 126 } 127 if (destline >= LINES) { 128 l = destline; 129 destline = LINES - 1; 130 if (outline < LINES - 1) { 131 c = destcol; 132 if (__pfast == 0 && !cursor_address) 133 destcol = 0; 134 fgoto(in_refresh); 135 destcol = c; 136 } 137 while (l >= LINES) { 138 /* The following linefeed (or simulation thereof) is 139 * supposed to scroll up the screen, since we are on 140 * the bottom line. We make the assumption that 141 * linefeed will scroll. If ns is in the capability 142 * list this won't work. We should probably have an 143 * sc capability but sf will generally take the place 144 * if it works. 145 * 146 * Superbee glitch: in the middle of the screen have to 147 * use esc B (down) because linefeed screws up in 148 * "Efficient Paging" (what a joke) mode (which is 149 * essential in some SB's because CRLF mode puts 150 * garbage in at end of memory), but you must use 151 * linefeed to scroll since down arrow won't go past 152 * memory end. I turned this off after recieving Paul 153 * Eggert's Superbee description which wins better. */ 154 if (cursor_down /* && !__tc_xb */ && __pfast) 155 tputs(cursor_down, 0, __cputchar); 156 else 157 __cputchar('\n'); 158 l--; 159 if (__pfast == 0) 160 outcol = 0; 161 } 162 } 163 if (destline < outline && !(cursor_address || cursor_up)) 164 destline = outline; 165 166 if (cursor_address && 167 (cgp = tiparm(cursor_address, destline, destcol))) 168 { 169 /* 170 * Need this condition due to inconsistent behavior 171 * of backspace on the last column. 172 */ 173 __CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp); 174 if (outcol != COLS - 1 && 175 plod((int) strlen(cgp), in_refresh) > 0) 176 plod(0, in_refresh); 177 else 178 tputs(cgp, 0, __cputchar); 179 } else 180 plod(0, in_refresh); 181 outline = destline; 182 outcol = destcol; 183 } 184 185 /* 186 * Move (slowly) to destination. 187 * Hard thing here is using home cursor on really deficient terminals. 188 * Otherwise just use cursor motions, hacking use of tabs and overtabbing 189 * and backspace. 190 * 191 * XXX this needs to be revisited for wide characters since we may output 192 * XXX more than one byte for a character. 193 */ 194 195 static int plodcnt, plodflg; 196 197 static int 198 plodput(int c) 199 { 200 if (plodflg) 201 --plodcnt; 202 else 203 __cputchar(c); 204 return 0; 205 } 206 207 static int 208 plod(int cnt, int in_refresh) 209 { 210 int i, j, k, soutcol, soutline; 211 212 __CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n", 213 cnt, in_refresh); 214 __CTRACE(__CTRACE_OUTPUT, 215 "plod: plodding from col %d, row %d to col %d, row %d\n", 216 outcol, outline, destcol, destline); 217 plodcnt = plodflg = cnt; 218 soutcol = outcol; 219 soutline = outline; 220 221 /* 222 * Consider homing and moving down/right from there, vs. moving 223 * directly with local motions to the right spot. 224 */ 225 if (cursor_home) { 226 /* 227 * i is the cost to home and tab/space to the right to get to 228 * the proper column. This assumes nd space costs 1 char. So 229 * i + destcol is cost of motion with home. 230 */ 231 if (__GT) 232 i = (destcol / HARDTABS) + (destcol % HARDTABS); 233 else 234 i = destcol; 235 236 /* j is cost to move locally without homing. */ 237 if (destcol >= outcol) { /* if motion is to the right */ 238 j = destcol / HARDTABS - outcol / HARDTABS; 239 if (__GT && j) 240 j += destcol % HARDTABS; 241 else 242 j = destcol - outcol; 243 } else 244 /* leftward motion only works if we can backspace. */ 245 if (outcol - destcol <= i) 246 /* Cheaper to backspace. */ 247 i = j = outcol - destcol; 248 else 249 /* Impossibly expensive. */ 250 j = i + 1; 251 252 /* k is the absolute value of vertical distance. */ 253 k = outline - destline; 254 if (k < 0) 255 k = -k; 256 j += k; 257 258 /* Decision. We may not have a choice if no up. */ 259 if (i + destline < j || (!cursor_up && destline < outline)) { 260 /* 261 * Cheaper to home. Do it now and pretend it's a 262 * regular local motion. 263 */ 264 tputs(cursor_home, 0, plodput); 265 outcol = outline = 0; 266 } else 267 if (cursor_to_ll) { 268 /* 269 * Quickly consider homing down and moving from 270 * there. Assume cost of ll is 2. 271 */ 272 k = (LINES - 1) - destline; 273 if (i + k + 2 < j && (k <= 0 || cursor_up)) { 274 tputs(cursor_to_ll, 0, plodput); 275 outcol = 0; 276 outline = LINES - 1; 277 } 278 } 279 } else 280 /* No home and no up means it's impossible. */ 281 if (!cursor_up && destline < outline) 282 return (-1); 283 if (__GT) 284 i = destcol % HARDTABS + destcol / HARDTABS; 285 else 286 i = destcol; 287 #ifdef notdef 288 if (back_tab && outcol > destcol && 289 (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) { 290 j *= (k = strlen(back_tab)); 291 if ((k += (destcol & 7)) > 4) 292 j += 8 - (destcol & 7); 293 else 294 j += k; 295 } else 296 #endif 297 j = outcol - destcol; 298 299 /* 300 * If we will later need a \n which will turn into a \r\n by the 301 * system or the terminal, then don't bother to try to \r. 302 */ 303 if ((__NONL || !__pfast) && outline < destline) 304 goto dontcr; 305 306 /* 307 * If the terminal will do a \r\n and there isn't room for it, then 308 * we can't afford a \r. 309 */ 310 if (!carriage_return && outline >= destline) 311 goto dontcr; 312 313 /* 314 * If it will be cheaper, or if we can't back up, then send a return 315 * preliminarily. 316 */ 317 if (j > i + 1 || outcol > destcol) { 318 /* 319 * BUG: this doesn't take the (possibly long) length of cr 320 * into account. 321 */ 322 if (carriage_return) 323 tputs(carriage_return, 0, plodput); 324 else 325 plodput('\r'); 326 if (!carriage_return) { 327 if (cursor_down) 328 tputs(cursor_down, 0, plodput); 329 else 330 plodput('\n'); 331 outline++; 332 } 333 334 outcol = 0; 335 } 336 dontcr:while (outline < destline) { 337 outline++; 338 if (cursor_down) 339 tputs(cursor_down, 0, plodput); 340 else 341 plodput('\n'); 342 if (plodcnt < 0) 343 goto out; 344 /* 345 * If the terminal does a CR with NL or we are in 346 * a mode where a \n will result in an implicit \r 347 * then adjust the outcol to match iff we actually 348 * emitted said \n. 349 */ 350 if ((__NONL || __pfast == 0) && 351 (!cursor_down || (*cursor_down == '\n'))) 352 outcol = 0; 353 } 354 #ifdef notdef 355 if (back_tab) 356 k = (int) strlen(back_tab); 357 #endif 358 while (outcol > destcol) { 359 if (plodcnt < 0) 360 goto out; 361 #ifdef notdef 362 if (back_tab && outcol - destcol > k + 4) { 363 tputs(back_tab, 0, plodput); 364 outcol--; 365 outcol &= ~7; 366 continue; 367 } 368 #endif 369 outcol--; 370 if (cursor_left) 371 tputs(cursor_left, 0, plodput); 372 else 373 plodput('\b'); 374 } 375 while (outline > destline) { 376 outline--; 377 tputs(cursor_up, 0, plodput); 378 if (plodcnt < 0) 379 goto out; 380 } 381 if (__GT && destcol - outcol > 1) { 382 for (;;) { 383 i = tabcol(outcol, HARDTABS); 384 if (i > destcol) 385 break; 386 if (tab) 387 tputs(tab, 0, plodput); 388 else 389 plodput('\t'); 390 outcol = i; 391 } 392 if (destcol - outcol > 4 && i < COLS) { 393 if (tab) 394 tputs(tab, 0, plodput); 395 else 396 plodput('\t'); 397 outcol = i; 398 while (outcol > destcol) { 399 outcol--; 400 if (cursor_left) 401 tputs(cursor_left, 0, plodput); 402 else 403 plodput('\b'); 404 } 405 } 406 } 407 while (outcol < destcol) { 408 /* 409 * Move one char to the right. We don't use nd space because 410 * it's better to just print the char we are moving over. 411 */ 412 if (in_refresh) 413 if (plodflg) /* Avoid a complex calculation. */ 414 plodcnt--; 415 else { 416 #ifndef HAVE_WCHAR 417 i = curscr->alines[outline]->line[outcol].ch 418 & __CHARTEXT; 419 if (curscr->alines[outline]->line[outcol].attr 420 == curscr->wattr) 421 __cputchar(i); 422 #else 423 if ((curscr->alines[outline]->line[outcol].attr 424 & WA_ATTRIBUTES) 425 == curscr->wattr) { 426 switch (WCOL(curscr->alines[outline]->line[outcol])) { 427 case 1: 428 __cputwchar(curscr->alines[outline]->line[outcol].ch); 429 __cursesi_putnsp(curscr->alines[outline]->line[outcol].nsp, 430 outline, 431 outcol); 432 __CTRACE(__CTRACE_OUTPUT, 433 "plod: (%d,%d)WCOL(%d), " 434 "putwchar(%x)\n", 435 outline, outcol, 436 WCOL(curscr->alines[outline]->line[outcol]), 437 curscr->alines[outline]->line[outcol].ch); 438 /*FALLTHROUGH*/ 439 case 0: 440 break; 441 default: 442 goto nondes; 443 } 444 } 445 #endif /* HAVE_WCHAR */ 446 else 447 goto nondes; 448 } 449 else 450 nondes: if (cursor_right) 451 tputs(cursor_right, 0, plodput); 452 else 453 plodput(' '); 454 outcol++; 455 if (plodcnt < 0) 456 goto out; 457 } 458 459 out: if (plodflg) { 460 outcol = soutcol; 461 outline = soutline; 462 } 463 __CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt); 464 return plodcnt; 465 } 466 467 /* 468 * Return the column number that results from being in column col and 469 * hitting a tab, where tabs are set every ts columns. Work right for 470 * the case where col > COLS, even if ts does not divide COLS. 471 */ 472 static int 473 tabcol(int col, int ts) 474 { 475 int offset; 476 477 if (col >= COLS) { 478 offset = COLS * (col / COLS); 479 col -= offset; 480 } else 481 offset = 0; 482 return (col + ts - (col % ts) + offset); 483 } 484