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