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