1 /* $NetBSD: cr_put.c,v 1.27 2008/07/23 13:32:41 tnozaki 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.27 2008/07/23 13:32:41 tnozaki 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 __P((int)); 62 static int plod __P((int, int)); 63 static int plodput __P((int)); 64 static int tabcol __P((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)\n", ly, lx, y, x); 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(in_refresh) 90 int in_refresh; 91 { 92 int c, l; 93 char cgp[128]; 94 95 #ifdef DEBUG 96 __CTRACE(__CTRACE_OUTPUT, "fgoto: in_refresh=%d\n", in_refresh); 97 #endif /* DEBUG */ 98 if (destcol >= COLS) { 99 destline += destcol / COLS; 100 destcol %= COLS; 101 } 102 if (outcol >= COLS) { 103 l = (outcol + 1) / COLS; 104 outline += l; 105 outcol %= COLS; 106 if (__tc_am == 0) { 107 while (l > 0) { 108 if (__pfast) { 109 if (__tc_cr) 110 tputs(__tc_cr, 0, __cputchar); 111 else 112 __cputchar('\r'); 113 } 114 if (__tc_nl) 115 tputs(__tc_nl, 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 && !__CA) 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 (__tc_nl /* && !__tc_xb */ && __pfast) 155 tputs(__tc_nl, 0, __cputchar); 156 else 157 __cputchar('\n'); 158 l--; 159 if (__pfast == 0) 160 outcol = 0; 161 } 162 } 163 if (destline < outline && !(__CA || __tc_up)) 164 destline = outline; 165 if (__CA && t_goto(NULL, __tc_cm, destcol, destline, cgp, 166 sizeof(cgp) - 1) != -1) { 167 /* 168 * Need this condition due to inconsistent behavior 169 * of backspace on the last column. 170 */ 171 #ifdef DEBUG 172 __CTRACE(__CTRACE_OUTPUT, "fgoto: cgp=%s\n", cgp); 173 #endif /* DEBUG */ 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 * Move (slowly) to destination. 186 * Hard thing here is using home cursor on really deficient terminals. 187 * Otherwise just use cursor motions, hacking use of tabs and overtabbing 188 * and backspace. 189 * 190 * XXX this needs to be revisited for wide characters since we may output 191 * XXX more than one byte for a character. 192 */ 193 194 static int plodcnt, plodflg; 195 196 static int 197 plodput(c) 198 int c; 199 { 200 if (plodflg) 201 --plodcnt; 202 else 203 __cputchar(c); 204 return (0); 205 } 206 207 static int 208 plod(cnt, in_refresh) 209 int cnt, in_refresh; 210 { 211 int i, j, k, soutcol, soutline; 212 213 #ifdef DEBUG 214 __CTRACE(__CTRACE_OUTPUT, "plod: cnt=%d, in_refresh=%d\n", 215 cnt, in_refresh); 216 #endif /* DEBUG */ 217 plodcnt = plodflg = cnt; 218 soutcol = outcol; 219 soutline = outline; 220 /* 221 * Consider homing and moving down/right from there, vs. moving 222 * directly with local motions to the right spot. 223 */ 224 if (__tc_ho) { 225 /* 226 * i is the cost to home and tab/space to the right to get to 227 * the proper column. This assumes nd space costs 1 char. So 228 * i + destcol is cost of motion with home. 229 */ 230 if (__GT) 231 i = (destcol / HARDTABS) + (destcol % HARDTABS); 232 else 233 i = destcol; 234 235 /* j is cost to move locally without homing. */ 236 if (destcol >= outcol) { /* if motion is to the right */ 237 j = destcol / HARDTABS - outcol / HARDTABS; 238 if (__GT && j) 239 j += destcol % HARDTABS; 240 else 241 j = destcol - outcol; 242 } else 243 /* leftward motion only works if we can backspace. */ 244 if (outcol - destcol <= i && (__tc_bs || __tc_bc)) 245 /* Cheaper to backspace. */ 246 i = j = outcol - destcol; 247 else 248 /* Impossibly expensive. */ 249 j = i + 1; 250 251 /* k is the absolute value of vertical distance. */ 252 k = outline - destline; 253 if (k < 0) 254 k = -k; 255 j += k; 256 257 /* Decision. We may not have a choice if no up. */ 258 if (i + destline < j || (!__tc_up && destline < outline)) { 259 /* 260 * Cheaper to home. Do it now and pretend it's a 261 * regular local motion. 262 */ 263 tputs(__tc_ho, 0, plodput); 264 outcol = outline = 0; 265 } else 266 if (__tc_ll) { 267 /* 268 * Quickly consider homing down and moving from 269 * there. Assume cost of ll is 2. 270 */ 271 k = (LINES - 1) - destline; 272 if (i + k + 2 < j && (k <= 0 || __tc_up)) { 273 tputs(__tc_ll, 0, plodput); 274 outcol = 0; 275 outline = LINES - 1; 276 } 277 } 278 } else 279 /* No home and no up means it's impossible. */ 280 if (!__tc_up && destline < outline) 281 return (-1); 282 if (__GT) 283 i = destcol % HARDTABS + destcol / HARDTABS; 284 else 285 i = destcol; 286 #ifdef notdef 287 if (__tc_bt && outcol > destcol && 288 (j = (((outcol + 7) & ~7) - destcol - 1) >> 3)) { 289 j *= (k = strlen(__tc_bt)); 290 if ((k += (destcol & 7)) > 4) 291 j += 8 - (destcol & 7); 292 else 293 j += k; 294 } else 295 #endif 296 j = outcol - destcol; 297 298 /* 299 * If we will later need a \n which will turn into a \r\n by the 300 * system or the terminal, then don't bother to try to \r. 301 */ 302 if ((__NONL || !__pfast) && outline < destline) 303 goto dontcr; 304 305 /* 306 * If the terminal will do a \r\n and there isn't room for it, then 307 * we can't afford a \r. 308 */ 309 if (__tc_nc && outline >= destline) 310 goto dontcr; 311 312 /* 313 * If it will be cheaper, or if we can't back up, then send a return 314 * preliminarily. 315 */ 316 if (j > i + 1 || (outcol > destcol && !__tc_bs && !__tc_bc)) { 317 /* 318 * BUG: this doesn't take the (possibly long) length of cr 319 * into account. 320 */ 321 if (__tc_cr) 322 tputs(__tc_cr, 0, plodput); 323 else 324 plodput('\r'); 325 if (__tc_nc) { 326 if (__tc_nl) 327 tputs(__tc_nl, 0, plodput); 328 else 329 plodput('\n'); 330 outline++; 331 } 332 outcol = 0; 333 } 334 dontcr:while (outline < destline) { 335 outline++; 336 if (__tc_nl) 337 tputs(__tc_nl, 0, plodput); 338 else 339 plodput('\n'); 340 if (plodcnt < 0) 341 goto out; 342 if (__NONL || __pfast == 0) 343 outcol = 0; 344 } 345 if (__tc_bt) 346 k = (int) strlen(__tc_bt); 347 while (outcol > destcol) { 348 if (plodcnt < 0) 349 goto out; 350 #ifdef notdef 351 if (__tc_bt && outcol - destcol > k + 4) { 352 tputs(__tc_bt, 0, plodput); 353 outcol--; 354 outcol &= ~7; 355 continue; 356 } 357 #endif 358 outcol--; 359 if (__tc_bc) 360 tputs(__tc_bc, 0, plodput); 361 else 362 plodput('\b'); 363 } 364 while (outline > destline) { 365 outline--; 366 tputs(__tc_up, 0, plodput); 367 if (plodcnt < 0) 368 goto out; 369 } 370 if (__GT && destcol - outcol > 1) { 371 for (;;) { 372 i = tabcol(outcol, HARDTABS); 373 if (i > destcol) 374 break; 375 if (__tc_ta) 376 tputs(__tc_ta, 0, plodput); 377 else 378 plodput('\t'); 379 outcol = i; 380 } 381 if (destcol - outcol > 4 && i < COLS && (__tc_bc || __tc_bs)) { 382 if (__tc_ta) 383 tputs(__tc_ta, 0, plodput); 384 else 385 plodput('\t'); 386 outcol = i; 387 while (outcol > destcol) { 388 outcol--; 389 if (__tc_bc) 390 tputs(__tc_bc, 0, plodput); 391 else 392 plodput('\b'); 393 } 394 } 395 } 396 while (outcol < destcol) { 397 /* 398 * Move one char to the right. We don't use nd space because 399 * it's better to just print the char we are moving over. 400 */ 401 if (in_refresh) 402 if (plodflg) /* Avoid a complex calculation. */ 403 plodcnt--; 404 else { 405 #ifndef HAVE_WCHAR 406 i = curscr->lines[outline]->line[outcol].ch 407 & __CHARTEXT; 408 if (curscr->lines[outline]->line[outcol].attr 409 == curscr->wattr) 410 __cputchar(i); 411 #else 412 if ((curscr->lines[outline]->line[outcol].attr 413 & WA_ATTRIBUTES) 414 == curscr->wattr) { 415 switch (WCOL(curscr->lines[outline]->line[outcol])) { 416 case 1: 417 __cputwchar(curscr->lines[outline]->line[outcol].ch); 418 __cursesi_putnsp(curscr->lines[outline]->line[outcol].nsp, 419 outline, 420 outcol); 421 #ifdef DEBUG 422 __CTRACE(__CTRACE_OUTPUT, 423 "plod: (%d,%d)WCOL(%d), " 424 "putwchar(%x)\n", 425 outline, outcol, 426 WCOL(curscr->lines[outline]->line[outcol]), 427 curscr->lines[outline]->line[outcol].ch); 428 #endif /* DEBUG */ 429 /*FALLTHROUGH*/ 430 case 0: 431 break; 432 default: 433 goto nondes; 434 } 435 } 436 #endif /* HAVE_WCHAR */ 437 else 438 goto nondes; 439 } 440 else 441 nondes: if (__tc_nd) 442 tputs(__tc_nd, 0, plodput); 443 else 444 plodput(' '); 445 outcol++; 446 if (plodcnt < 0) 447 goto out; 448 } 449 450 out: if (plodflg) { 451 outcol = soutcol; 452 outline = soutline; 453 } 454 #ifdef DEBUG 455 __CTRACE(__CTRACE_OUTPUT, "plod: returns %d\n", plodcnt); 456 #endif /* DEBUG */ 457 return (plodcnt); 458 } 459 /* 460 * Return the column number that results from being in column col and 461 * hitting a tab, where tabs are set every ts columns. Work right for 462 * the case where col > COLS, even if ts does not divide COLS. 463 */ 464 static int 465 tabcol(col, ts) 466 int col, ts; 467 { 468 int offset; 469 470 if (col >= COLS) { 471 offset = COLS * (col / COLS); 472 col -= offset; 473 } else 474 offset = 0; 475 return (col + ts - (col % ts) + offset); 476 } 477