Home | History | Annotate | Line # | Download | only in libcurses
addbytes.c revision 1.34
      1 /*	$NetBSD: addbytes.c,v 1.34 2008/07/04 16:52:10 tnozaki Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1987, 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[] = "@(#)addbytes.c	8.4 (Berkeley) 5/4/94";
     36 #else
     37 __RCSID("$NetBSD: addbytes.c,v 1.34 2008/07/04 16:52:10 tnozaki Exp $");
     38 #endif
     39 #endif				/* not lint */
     40 
     41 #include <stdlib.h>
     42 #include "curses.h"
     43 #include "curses_private.h"
     44 #ifdef DEBUG
     45 #include <assert.h>
     46 #endif
     47 
     48 #define	SYNCH_IN	{y = win->cury; x = win->curx;}
     49 #define	SYNCH_OUT	{win->cury = y; win->curx = x;}
     50 #define	PSYNCH_IN	{*y = win->cury; *x = win->curx;}
     51 #define	PSYNCH_OUT	{win->cury = *y; win->curx = *x;}
     52 
     53 #ifndef _CURSES_USE_MACROS
     54 
     55 /*
     56  * addbytes --
     57  *      Add the character to the current position in stdscr.
     58  */
     59 int
     60 addbytes(const char *bytes, int count)
     61 {
     62 	return __waddbytes(stdscr, bytes, count, 0);
     63 }
     64 
     65 /*
     66  * waddbytes --
     67  *      Add the character to the current position in the given window.
     68  */
     69 int
     70 waddbytes(WINDOW *win, const char *bytes, int count)
     71 {
     72 	return __waddbytes(win, bytes, count, 0);
     73 }
     74 
     75 /*
     76  * mvaddbytes --
     77  *      Add the characters to stdscr at the location given.
     78  */
     79 int
     80 mvaddbytes(int y, int x, const char *bytes, int count)
     81 {
     82 	return mvwaddbytes(stdscr, y, x, bytes, count);
     83 }
     84 
     85 /*
     86  * mvwaddbytes --
     87  *      Add the characters to the given window at the location given.
     88  */
     89 int
     90 mvwaddbytes(WINDOW *win, int y, int x, const char *bytes, int count)
     91 {
     92 	if (wmove(win, y, x) == ERR)
     93 		return ERR;
     94 
     95 	return __waddbytes(win, bytes, count, 0);
     96 }
     97 
     98 #endif
     99 
    100 /*
    101  * waddbytes --
    102  *	Add the character to the current position in the given window.
    103  */
    104 int
    105 __waddbytes(WINDOW *win, const char *bytes, int count, attr_t attr)
    106 {
    107 	int		 x, y, err;
    108 	__LINE		*lp;
    109 #ifdef HAVE_WCHAR
    110 	int		n;
    111 	cchar_t		cc;
    112 	wchar_t		wc;
    113 	mbstate_t	st;
    114 #else
    115 	int		c;
    116 #endif
    117 #ifdef DEBUG
    118 	int             i;
    119 
    120 	for (i = 0; i < win->maxy; i++) {
    121 		assert(win->lines[i]->sentinel == SENTINEL_VALUE);
    122 	}
    123 
    124 	__CTRACE(__CTRACE_INPUT, "ADDBYTES: add %d bytes\n", count);
    125 #endif
    126 
    127 	err = OK;
    128 	SYNCH_IN;
    129 	lp = win->lines[y];
    130 
    131 #ifdef HAVE_WCHAR
    132 	(void)mbrtowc(NULL, NULL, (size_t)0, &st);
    133 #endif
    134 	while (count > 0) {
    135 #ifndef HAVE_WCHAR
    136 		c = *bytes++;
    137 #ifdef DEBUG
    138 		__CTRACE(__CTRACE_INPUT, "ADDBYTES('%c', %x) at (%d, %d)\n",
    139 		    c, attr, y, x);
    140 #endif
    141 		err = _cursesi_addbyte(win, &lp, &y, &x, c, attr);
    142 		count--;
    143 #else
    144 		/*
    145 		 * For wide character support only, try and convert the
    146 		 * given string into a wide character - we do this because
    147 		 * this is how ncurses behaves (not that I think this is
    148 		 * actually the correct thing to do but if we don't do it
    149 		 * a lot of things that rely on this behaviour will break
    150 		 * and we will be blamed).  If the conversion succeeds
    151 		 * then we eat the n characters used to make the wide char
    152 		 * from the string.
    153 		 */
    154 		n = (int)mbrtowc(&wc, bytes, (size_t)count, &st);
    155 		if (n < 0) {
    156 			/* not a valid conversion just eat a char */
    157 			wc = *bytes;
    158 			n = 1;
    159 			(void)mbrtowc(NULL, NULL, (size_t)0, &st);
    160 		} else if (wc == 0) {
    161 			break;
    162 		}
    163 #ifdef DEBUG
    164 	__CTRACE(__CTRACE_INPUT,
    165 		 "ADDBYTES WIDE(0x%x [%s], %x) at (%d, %d), ate %d bytes\n",
    166 		 (unsigned) wc, unctrl((unsigned) wc), attr, y, x, n);
    167 #endif
    168 		cc.vals[0] = wc;
    169 		cc.elements = 1;
    170 		cc.attributes = attr;
    171 		err = _cursesi_addwchar(win, &lp, &y, &x, &cc);
    172 		bytes += n;
    173 		count -= n;
    174 #endif
    175 	}
    176 
    177 	SYNCH_OUT;
    178 
    179 #ifdef DEBUG
    180 	for (i = 0; i < win->maxy; i++) {
    181 		assert(win->lines[i]->sentinel == SENTINEL_VALUE);
    182 	}
    183 #endif
    184 
    185 	return (err);
    186 }
    187 
    188 /*
    189  * _cursesi_addbyte -
    190  *	Internal function to add a byte and update the row and column
    191  * positions as appropriate.  This function is only used in the narrow
    192  * character version of curses.
    193  */
    194 int
    195 _cursesi_addbyte(WINDOW *win, __LINE **lp, int *y, int *x, int c,
    196 		 attr_t attr)
    197 {
    198 	static char	 blanks[] = "        ";
    199 	int		 newx;
    200 	attr_t		 attributes;
    201 
    202 	switch (c) {
    203 	case '\t':
    204 		PSYNCH_OUT;
    205 		if (waddbytes(win, blanks, 8 - (*x % 8)) == ERR)
    206 			return (ERR);
    207 		PSYNCH_IN;
    208 		break;
    209 
    210 	default:
    211 #ifdef DEBUG
    212 		__CTRACE(__CTRACE_INPUT, "ADDBYTES(%p, %d, %d)\n",
    213 			 win, *y, *x);
    214 #endif
    215 
    216 		if ((*lp)->flags & __ISPASTEOL) {
    217 		  newline:
    218 			*x = 0;
    219 			(*lp)->flags &= ~__ISPASTEOL;
    220 			if (*y == win->scr_b) {
    221 #ifdef DEBUG
    222 				__CTRACE(__CTRACE_INPUT,
    223 					 "ADDBYTES - on bottom "
    224 					 "of scrolling region\n");
    225 #endif
    226 				if (!(win->flags & __SCROLLOK))
    227 					return ERR;
    228 				PSYNCH_OUT;
    229 				scroll(win);
    230 				PSYNCH_IN;
    231 			} else {
    232 				(*y)++;
    233 			}
    234 			*lp = win->lines[*y];
    235 			if (c == '\n')
    236 				break;
    237 		}
    238 
    239 		attributes = (win->wattr | attr) &
    240 			(__ATTRIBUTES & ~__COLOR);
    241 		if (attr & __COLOR)
    242 			attributes |= attr & __COLOR;
    243 		else if (win->wattr & __COLOR)
    244 			attributes |= win->wattr & __COLOR;
    245 #ifdef DEBUG
    246 		__CTRACE(__CTRACE_INPUT,
    247 			 "ADDBYTES: 1: y = %d, x = %d, firstch = %d, "
    248 			 "lastch = %d\n",
    249 			 *y, *x, *win->lines[*y]->firstchp,
    250 			 *win->lines[*y]->lastchp);
    251 #endif
    252 		/*
    253 		 * Always update the change pointers.  Otherwise,
    254 		 * we could end up not displaying 'blank' characters
    255 		 * when overlapping windows are displayed.
    256 		 */
    257 		newx = *x + win->ch_off;
    258 		(*lp)->flags |= __ISDIRTY;
    259 		/*
    260 		 * firstchp/lastchp are shared between
    261 		 * parent window and sub-window.
    262 		 */
    263 		if (newx < *(*lp)->firstchp)
    264 			*(*lp)->firstchp = newx;
    265 		if (newx > *(*lp)->lastchp)
    266 			*(*lp)->lastchp = newx;
    267 #ifdef DEBUG
    268 		__CTRACE(__CTRACE_INPUT,
    269 			 "ADDBYTES: change gives f/l: %d/%d [%d/%d]\n",
    270 			 *(*lp)->firstchp, *(*lp)->lastchp,
    271 			 *(*lp)->firstchp - win->ch_off,
    272 			 *(*lp)->lastchp - win->ch_off);
    273 #endif
    274 		if (win->bch != ' ' && c == ' ')
    275 			(*lp)->line[*x].ch = win->bch;
    276 		else
    277 			(*lp)->line[*x].ch = c;
    278 
    279 		if (attributes & __COLOR)
    280 			(*lp)->line[*x].attr =
    281 				attributes | (win->battr & ~__COLOR);
    282 		else
    283 			(*lp)->line[*x].attr = attributes | win->battr;
    284 
    285 		if (*x == win->maxx - 1)
    286 			(*lp)->flags |= __ISPASTEOL;
    287 		else
    288 			(*x)++;
    289 #ifdef DEBUG
    290 		__CTRACE(__CTRACE_INPUT,
    291 			 "ADDBYTES: 2: y = %d, x = %d, firstch = %d, "
    292 			 "lastch = %d\n",
    293 			 *y, *x, *win->lines[*y]->firstchp,
    294 			 *win->lines[*y]->lastchp);
    295 #endif
    296 		break;
    297 	case '\n':
    298 		PSYNCH_OUT;
    299 		wclrtoeol(win);
    300 		PSYNCH_IN;
    301 		goto newline;
    302 	case '\r':
    303 		*x = 0;
    304 		break;
    305 	case '\b':
    306 		if (--(*x) < 0)
    307 			*x = 0;
    308 		break;
    309 	}
    310 
    311 	return (OK);
    312 }
    313 
    314 /*
    315  * _cursesi_addwchar -
    316  *	Internal function to add a wide character and update the row
    317  * and column positions.
    318  */
    319 int
    320 _cursesi_addwchar(WINDOW *win, __LINE **lnp, int *y, int *x,
    321 		  const cchar_t *wch)
    322 {
    323 #ifndef HAVE_WCHAR
    324 	return (ERR);
    325 #else
    326 	int sx = 0, ex = 0, cw = 0, i = 0, newx = 0;
    327 	__LDATA *lp = &win->lines[*y]->line[*x], *tp = NULL;
    328 	nschar_t *np = NULL;
    329 	cchar_t cc;
    330 	attr_t attributes;
    331 
    332 	/* special characters handling */
    333 	switch (wch->vals[0]) {
    334 	case L'\b':
    335 		if (--*x < 0)
    336 			*x = 0;
    337 		win->curx = *x;
    338 		return OK;
    339 	case L'\r':
    340 		*x = 0;
    341 		return OK;
    342 	case L'\n':
    343 		wclrtoeol(win);
    344 		PSYNCH_IN;
    345 		*x = 0;
    346 		(*lnp)->flags &= ~__ISPASTEOL;
    347 		if (*y == win->scr_b) {
    348 			if (!(win->flags & __SCROLLOK))
    349 				return ERR;
    350 			PSYNCH_OUT;
    351 			scroll(win);
    352 			PSYNCH_IN;
    353 		} else {
    354 			(*y)++;
    355 		}
    356 		PSYNCH_OUT;
    357 		return OK;
    358 	case L'\t':
    359 		cc.vals[0] = L' ';
    360 		cc.elements = 1;
    361 		cc.attributes = win->wattr;
    362 		for (i = 0; i < 8 - (*x % 8); i++) {
    363 			if (wadd_wch(win, &cc) == ERR)
    364 				return ERR;
    365 		}
    366 		return OK;
    367 	}
    368 
    369 	/* check for non-spacing character */
    370 	if (!wcwidth(wch->vals[0])) {
    371 #ifdef DEBUG
    372 		__CTRACE(__CTRACE_INPUT,
    373 			 "_cursesi_addwchar: char '%c' is non-spacing\n",
    374 			 wch->vals[0]);
    375 #endif /* DEBUG */
    376 		cw = WCOL(*lp);
    377 		if (cw < 0) {
    378 			lp += cw;
    379 			*x += cw;
    380 		}
    381 		for (i = 0; i < wch->elements; i++) {
    382 			if (!(np = (nschar_t *) malloc(sizeof(nschar_t))))
    383 				return ERR;;
    384 			np->ch = wch->vals[i];
    385 			np->next = lp->nsp;
    386 			lp->nsp = np;
    387 		}
    388 		(*lnp)->flags |= __ISDIRTY;
    389 		newx = *x + win->ch_off;
    390 		if (newx < *(*lnp)->firstchp)
    391 			*(*lnp)->firstchp = newx;
    392 		if (newx > *(*lnp)->lastchp)
    393 			*(*lnp)->lastchp = newx;
    394 		__touchline(win, *y, *x, *x);
    395 		return OK;
    396 	}
    397 	/* check for new line first */
    398 	if ((*lnp)->flags & __ISPASTEOL) {
    399 		*x = 0;
    400 		(*lnp)->flags &= ~__ISPASTEOL;
    401 		if (*y == win->scr_b) {
    402 			if (!(win->flags & __SCROLLOK))
    403 				return ERR;
    404 			PSYNCH_OUT;
    405 			scroll(win);
    406 			PSYNCH_IN;
    407 		} else {
    408 			(*y)++;
    409 		}
    410 		(*lnp) = win->lines[*y];
    411 		lp = &win->lines[*y]->line[*x];
    412 	}
    413 	/* clear out the current character */
    414 	cw = WCOL(*lp);
    415 	if (cw >= 0) {
    416 		sx = *x;
    417 	} else {
    418 		for (sx = *x - 1; sx >= max(*x + cw, 0); sx--) {
    419 #ifdef DEBUG
    420 			__CTRACE(__CTRACE_INPUT,
    421 				 "_cursesi_addwchar: clear current char (%d,%d)\n",
    422 				 *y, sx);
    423 #endif /* DEBUG */
    424 			tp = &win->lines[*y]->line[sx];
    425 			tp->ch = (wchar_t) btowc((int) win->bch);
    426 			if (_cursesi_copy_nsp(win->bnsp, tp) == ERR)
    427 				return ERR;
    428 
    429 			tp->attr = win->battr;
    430 			SET_WCOL(*tp, 1);
    431 		}
    432 		sx = *x + cw;
    433 		(*lnp)->flags |= __ISDIRTY;
    434 		newx = sx + win->ch_off;
    435 		if (newx < *(*lnp)->firstchp)
    436 			*(*lnp)->firstchp = newx;
    437 	}
    438 
    439 	/* check for enough space before the end of line */
    440 	cw = wcwidth(wch->vals[0]);
    441 	if (cw > win->maxx - *x) {
    442 #ifdef DEBUG
    443 		__CTRACE(__CTRACE_INPUT,
    444 			 "_cursesi_addwchar: clear EOL (%d,%d)\n",
    445 			 *y, *x);
    446 #endif /* DEBUG */
    447 		(*lnp)->flags |= __ISDIRTY;
    448 		newx = *x + win->ch_off;
    449 		if (newx < *(*lnp)->firstchp)
    450 			*(*lnp)->firstchp = newx;
    451 		for (tp = lp; *x < win->maxx; tp++, (*x)++) {
    452 			tp->ch = (wchar_t) btowc((int) win->bch);
    453 			if (_cursesi_copy_nsp(win->bnsp, tp) == ERR)
    454 				return ERR;
    455 			tp->attr = win->battr;
    456 			SET_WCOL(*tp, 1);
    457 		}
    458 		newx = win->maxx - 1 + win->ch_off;
    459 		if (newx > *(*lnp)->lastchp)
    460 			*(*lnp)->lastchp = newx;
    461 		__touchline(win, *y, sx, (int) win->maxx - 1);
    462 		sx = *x = 0;
    463 		if (*y == win->scr_b) {
    464 			if (!(win->flags & __SCROLLOK))
    465 				return ERR;
    466 			PSYNCH_OUT;
    467 			scroll(win);
    468 			PSYNCH_IN;
    469 		} else {
    470 			(*y)++;
    471 		}
    472 		lp = &win->lines[*y]->line[0];
    473 		(*lnp) = win->lines[*y];
    474 	}
    475 	win->cury = *y;
    476 
    477 	/* add spacing character */
    478 #ifdef DEBUG
    479 	__CTRACE(__CTRACE_INPUT,
    480 		 "_cursesi_addwchar: add character (%d,%d) 0x%x\n",
    481 		 *y, *x, wch->vals[0]);
    482 #endif /* DEBUG */
    483 	(*lnp)->flags |= __ISDIRTY;
    484 	newx = *x + win->ch_off;
    485 	if (newx < *(*lnp)->firstchp)
    486 		*(*lnp)->firstchp = newx;
    487 	if (lp->nsp) {
    488 		__cursesi_free_nsp(lp->nsp);
    489 		lp->nsp = NULL;
    490 	}
    491 
    492 	lp->ch = wch->vals[0];
    493 
    494 	attributes = (win->wattr | wch->attributes)
    495 		& (WA_ATTRIBUTES & ~__COLOR);
    496 	if (wch->attributes & __COLOR)
    497 		attributes |= wch->attributes & __COLOR;
    498 	else if (win->wattr & __COLOR)
    499 		attributes |= win->wattr & __COLOR;
    500 	if (attributes & __COLOR)
    501 		lp->attr = attributes | (win->battr & ~__COLOR);
    502 	else
    503 		lp->attr = attributes | win->battr;
    504 
    505 	SET_WCOL(*lp, cw);
    506 
    507 #ifdef DEBUG
    508 	__CTRACE(__CTRACE_INPUT,
    509 		 "_cursesi_addwchar: add spacing char 0x%x, attr 0x%x\n",
    510 		 lp->ch, lp->attr);
    511 #endif /* DEBUG */
    512 
    513 	if (wch->elements > 1) {
    514 		for (i = 1; i < wch->elements; i++) {
    515 			np = (nschar_t *)malloc(sizeof(nschar_t));
    516 			if (!np)
    517 				return ERR;;
    518 			np->ch = wch->vals[i];
    519 			np->next = lp->nsp;
    520 #ifdef DEBUG
    521 			__CTRACE(__CTRACE_INPUT,
    522 			    "_cursesi_addwchar: add non-spacing char 0x%x\n", np->ch);
    523 #endif /* DEBUG */
    524 			lp->nsp = np;
    525 		}
    526 	}
    527 #ifdef DEBUG
    528 	__CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: non-spacing list header: %p\n",
    529 	    lp->nsp);
    530 	__CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: add rest columns (%d:%d)\n",
    531 		sx + 1, sx + cw - 1);
    532 #endif /* DEBUG */
    533 	for (tp = lp + 1, *x = sx + 1; *x - sx <= cw - 1; tp++, (*x)++) {
    534 		if (tp->nsp) {
    535 			__cursesi_free_nsp(tp->nsp);
    536 			tp->nsp = NULL;
    537 		}
    538 		tp->ch = wch->vals[0];
    539 		tp->attr = lp->attr & WA_ATTRIBUTES;
    540 		/* Mark as "continuation" cell */
    541 		tp->attr |= __WCWIDTH;
    542 	}
    543 	if (*x == win->maxx) {
    544 		(*lnp)->flags |= __ISPASTEOL;
    545 		newx = win->maxx - 1 + win->ch_off;
    546 		if (newx > *(*lnp)->lastchp)
    547 			*(*lnp)->lastchp = newx;
    548 		__touchline(win, *y, sx, (int) win->maxx - 1);
    549 		win->curx = sx;
    550 	} else {
    551 		win->curx = *x;
    552 
    553 		/* clear the remining of the current characer */
    554 		if (*x && *x < win->maxx) {
    555 			ex = sx + cw;
    556 			tp = &win->lines[*y]->line[ex];
    557 			while (ex < win->maxx && WCOL(*tp) < 0) {
    558 #ifdef DEBUG
    559 				__CTRACE(__CTRACE_INPUT,
    560 					 "_cursesi_addwchar: clear "
    561 					 "remaining of current char (%d,%d)nn",
    562 					 *y, ex);
    563 #endif /* DEBUG */
    564 				tp->ch = (wchar_t) btowc((int) win->bch);
    565 				if (_cursesi_copy_nsp(win->bnsp, tp) == ERR)
    566 					return ERR;
    567 				tp->attr = win->battr;
    568 				SET_WCOL(*tp, 1);
    569 				tp++, ex++;
    570 			}
    571 			newx = ex - 1 + win->ch_off;
    572 			if (newx > *(*lnp)->lastchp)
    573 				*(*lnp)->lastchp = newx;
    574 			__touchline(win, *y, sx, ex - 1);
    575 		}
    576 	}
    577 
    578 #ifdef DEBUG
    579 	__CTRACE(__CTRACE_INPUT, "add_wch: %d : 0x%x\n", lp->ch, lp->attr);
    580 #endif /* DEBUG */
    581 	return OK;
    582 #endif
    583 }
    584