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