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