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