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