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