1 /* $NetBSD: cr_put.c,v 1.34 2019/05/20 22:17:41 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.34 2019/05/20 22:17:41 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 #endif /* DEBUG */ 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 #ifdef DEBUG 174 __CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp); 175 #endif /* DEBUG */ 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 * XXX this needs to be revisited for wide characters since we may output 194 * XXX more than one byte for a character. 195 */ 196 197 static int plodcnt, plodflg; 198 199 static int 200 plodput(int c) 201 { 202 if (plodflg) 203 --plodcnt; 204 else 205 __cputchar(c); 206 return 0; 207 } 208 209 static int 210 plod(int cnt, int in_refresh) 211 { 212 int i, j, k, soutcol, soutline; 213 214 #ifdef DEBUG 215 __CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n", 216 cnt, in_refresh); 217 #endif /* DEBUG */ 218 plodcnt = plodflg = cnt; 219 soutcol = outcol; 220 soutline = outline; 221 222 /* 223 * Consider homing and moving down/right from there, vs. moving 224 * directly with local motions to the right spot. 225 */ 226 if (cursor_home) { 227 /* 228 * i is the cost to home and tab/space to the right to get to 229 * the proper column. This assumes nd space costs 1 char. So 230 * i + destcol is cost of motion with home. 231 */ 232 if (__GT) 233 i = (destcol / HARDTABS) + (destcol % HARDTABS); 234 else 235 i = destcol; 236 237 /* j is cost to move locally without homing. */ 238 if (destcol >= outcol) { /* if motion is to the right */ 239 j = destcol / HARDTABS - outcol / HARDTABS; 240 if (__GT && j) 241 j += destcol % HARDTABS; 242 else 243 j = destcol - outcol; 244 } else 245 /* leftward motion only works if we can backspace. */ 246 if (outcol - destcol <= i) 247 /* Cheaper to backspace. */ 248 i = j = outcol - destcol; 249 else 250 /* Impossibly expensive. */ 251 j = i + 1; 252 253 /* k is the absolute value of vertical distance. */ 254 k = outline - destline; 255 if (k < 0) 256 k = -k; 257 j += k; 258 259 /* Decision. We may not have a choice if no up. */ 260 if (i + destline < j || (!cursor_up && destline < outline)) { 261 /* 262 * Cheaper to home. Do it now and pretend it's a 263 * regular local motion. 264 */ 265 tputs(cursor_home, 0, plodput); 266 outcol = outline = 0; 267 } else 268 if (cursor_to_ll) { 269 /* 270 * Quickly consider homing down and moving from 271 * there. Assume cost of ll is 2. 272 */ 273 k = (LINES - 1) - destline; 274 if (i + k + 2 < j && (k <= 0 || cursor_up)) { 275 tputs(cursor_to_ll, 0, plodput); 276 outcol = 0; 277 outline = LINES - 1; 278 } 279 } 280 } else 281 /* No home and no up means it's impossible. */ 282 if (!cursor_up && destline < outline) 283 return (-1); 284 if (__GT) 285 i = destcol % HARDTABS + destcol / HARDTABS; 286 else 287 i = destcol; 288 #ifdef notdef 289 if (back_tab && outcol > destcol && 290 (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) { 291 j *= (k = strlen(back_tab)); 292 if ((k += (destcol & 7)) > 4) 293 j += 8 - (destcol & 7); 294 else 295 j += k; 296 } else 297 #endif 298 j = outcol - destcol; 299 300 /* 301 * If we will later need a \n which will turn into a \r\n by the 302 * system or the terminal, then don't bother to try to \r. 303 */ 304 if ((__NONL || !__pfast) && outline < destline) 305 goto dontcr; 306 307 /* 308 * If the terminal will do a \r\n and there isn't room for it, then 309 * we can't afford a \r. 310 */ 311 if (!carriage_return && outline >= destline) 312 goto dontcr; 313 314 /* 315 * If it will be cheaper, or if we can't back up, then send a return 316 * preliminarily. 317 */ 318 if (j > i + 1 || outcol > destcol) { 319 /* 320 * BUG: this doesn't take the (possibly long) length of cr 321 * into account. 322 */ 323 if (carriage_return) 324 tputs(carriage_return, 0, plodput); 325 else 326 plodput('\r'); 327 if (!carriage_return) { 328 if (cursor_down) 329 tputs(cursor_down, 0, plodput); 330 else 331 plodput('\n'); 332 outline++; 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 if (__NONL || __pfast == 0) 345 outcol = 0; 346 } 347 #ifdef notdef 348 if (back_tab) 349 k = (int) strlen(back_tab); 350 #endif 351 while (outcol > destcol) { 352 if (plodcnt < 0) 353 goto out; 354 #ifdef notdef 355 if (back_tab && outcol - destcol > k + 4) { 356 tputs(back_tab, 0, plodput); 357 outcol--; 358 outcol &= ~7; 359 continue; 360 } 361 #endif 362 outcol--; 363 if (cursor_left) 364 tputs(cursor_left, 0, plodput); 365 else 366 plodput('\b'); 367 } 368 while (outline > destline) { 369 outline--; 370 tputs(cursor_up, 0, plodput); 371 if (plodcnt < 0) 372 goto out; 373 } 374 if (__GT && destcol - outcol > 1) { 375 for (;;) { 376 i = tabcol(outcol, HARDTABS); 377 if (i > destcol) 378 break; 379 if (tab) 380 tputs(tab, 0, plodput); 381 else 382 plodput('\t'); 383 outcol = i; 384 } 385 if (destcol - outcol > 4 && i < COLS) { 386 if (tab) 387 tputs(tab, 0, plodput); 388 else 389 plodput('\t'); 390 outcol = i; 391 while (outcol > destcol) { 392 outcol--; 393 if (cursor_left) 394 tputs(cursor_left, 0, plodput); 395 else 396 plodput('\b'); 397 } 398 } 399 } 400 while (outcol < destcol) { 401 /* 402 * Move one char to the right. We don't use nd space because 403 * it's better to just print the char we are moving over. 404 */ 405 if (in_refresh) 406 if (plodflg) /* Avoid a complex calculation. */ 407 plodcnt--; 408 else { 409 #ifndef HAVE_WCHAR 410 i = curscr->alines[outline]->line[outcol].ch 411 & __CHARTEXT; 412 if (curscr->alines[outline]->line[outcol].attr 413 == curscr->wattr) 414 __cputchar(i); 415 #else 416 if ((curscr->alines[outline]->line[outcol].attr 417 & WA_ATTRIBUTES) 418 == curscr->wattr) { 419 switch (WCOL(curscr->alines[outline]->line[outcol])) { 420 case 1: 421 __cputwchar(curscr->alines[outline]->line[outcol].ch); 422 __cursesi_putnsp(curscr->alines[outline]->line[outcol].nsp, 423 outline, 424 outcol); 425 #ifdef DEBUG 426 __CTRACE(__CTRACE_OUTPUT, 427 "plod: (%d,%d)WCOL(%d), " 428 "putwchar(%x)\n", 429 outline, outcol, 430 WCOL(curscr->alines[outline]->line[outcol]), 431 curscr->alines[outline]->line[outcol].ch); 432 #endif /* DEBUG */ 433 /*FALLTHROUGH*/ 434 case 0: 435 break; 436 default: 437 goto nondes; 438 } 439 } 440 #endif /* HAVE_WCHAR */ 441 else 442 goto nondes; 443 } 444 else 445 nondes: if (cursor_right) 446 tputs(cursor_right, 0, plodput); 447 else 448 plodput(' '); 449 outcol++; 450 if (plodcnt < 0) 451 goto out; 452 } 453 454 out: if (plodflg) { 455 outcol = soutcol; 456 outline = soutline; 457 } 458 #ifdef DEBUG 459 __CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt); 460 #endif /* DEBUG */ 461 return plodcnt; 462 } 463 464 /* 465 * Return the column number that results from being in column col and 466 * hitting a tab, where tabs are set every ts columns. Work right for 467 * the case where col > COLS, even if ts does not divide COLS. 468 */ 469 static int 470 tabcol(int col, int ts) 471 { 472 int offset; 473 474 if (col >= COLS) { 475 offset = COLS * (col / COLS); 476 col -= offset; 477 } else 478 offset = 0; 479 return (col + ts - (col % ts) + offset); 480 } 481