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