1 /* $NetBSD: addbytes.c,v 1.64 2022/05/19 07:41:26 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.64 2022/05/19 07:41:26 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 __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%x [%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 */ 200 int 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 if (newx > *(*lp)->lastchp) 297 *(*lp)->lastchp = newx; 298 __CTRACE(__CTRACE_INPUT, "ADDBYTES: change gives f/l: %d/%d [%d/%d]\n", 299 *(*lp)->firstchp, *(*lp)->lastchp, 300 *(*lp)->firstchp - win->ch_off, 301 *(*lp)->lastchp - win->ch_off); 302 if (win->bch != ' ' && c == ' ') { 303 (*lp)->line[*x].ch = win->bch; 304 #ifdef HAVE_CHAR 305 (*lp)->line[*x].wcols = win->wcols; 306 #endif 307 } else { 308 (*lp)->line[*x].ch = c; 309 #ifdef HAVE_CHAR 310 (*lp)->line[*x].wcols = wcols; 311 #endif 312 } 313 314 (*lp)->line[*x].cflags &= ~ CA_BACKGROUND; 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 __CTRACE(__CTRACE_INPUT, 328 "ADDBYTES: 2: y = %d, x = %d, firstch = %d, lastch = %d\n", 329 *y, *x, *win->alines[*y]->firstchp, *win->alines[*y]->lastchp); 330 __sync(win); 331 return OK; 332 } 333 334 /* 335 * _cursesi_addwchar - 336 * Internal function to add a wide character and update the row 337 * and column positions. 338 */ 339 int 340 _cursesi_addwchar(WINDOW *win, __LINE **lnp, int *y, int *x, 341 const cchar_t *wch, int char_interp) 342 { 343 #ifndef HAVE_WCHAR 344 return ERR; 345 #else 346 int sx = 0, ex = 0, cw = 0, i = 0, newx = 0, tabsize; 347 __LDATA *lp = &win->alines[*y]->line[*x], *tp = NULL; 348 nschar_t *np = NULL; 349 cchar_t cc; 350 attr_t attributes; 351 352 if (char_interp) { 353 /* special characters handling */ 354 switch (wch->vals[0]) { 355 case L'\b': 356 if (--*x < 0) 357 *x = 0; 358 return OK; 359 case L'\r': 360 *x = 0; 361 return OK; 362 case L'\n': 363 if (*y == win->scr_b) { 364 if (!(win->flags & __SCROLLOK)) 365 return ERR; 366 wclrtoeol(win); 367 scroll(win); 368 } else { 369 wclrtoeol(win); 370 (*y)++; 371 } 372 *x = 0; 373 (*lnp)->flags &= ~__ISPASTEOL; 374 return OK; 375 case L'\t': 376 cc.vals[0] = L' '; 377 cc.elements = 1; 378 cc.attributes = win->wattr; 379 tabsize = win->screen->TABSIZE; 380 newx = tabsize - (*x % tabsize); 381 382 /* if at the bottom of the window and 383 not allowed to scroll then just do 384 what we can */ 385 if ((*y == win->scr_b) && 386 !(win->flags & __SCROLLOK)) { 387 if ((*lnp)->flags & __ISPASTEOL) { 388 return ERR; 389 } 390 391 if (*x + newx > win->maxx - 1) 392 newx = win->maxx - *x - 1; 393 } 394 395 for (i = 0; i < newx; i++) { 396 if (wadd_wch(win, &cc) == ERR) 397 return ERR; 398 } 399 return OK; 400 } 401 } 402 403 /* check for non-spacing character */ 404 if (!wcwidth(wch->vals[0])) { 405 __CTRACE(__CTRACE_INPUT, 406 "_cursesi_addwchar: char '%c' is non-spacing\n", 407 wch->vals[0]); 408 cw = (*lp).wcols; 409 if (cw < 0) { 410 lp += cw; 411 *x += cw; 412 } 413 for (i = 0; i < wch->elements; i++) { 414 if (!(np = (nschar_t *) malloc(sizeof(nschar_t)))) 415 return ERR; 416 np->ch = wch->vals[i]; 417 np->next = lp->nsp; 418 lp->nsp = np; 419 } 420 (*lnp)->flags |= __ISDIRTY; 421 newx = *x + win->ch_off; 422 if (newx < *(*lnp)->firstchp) 423 *(*lnp)->firstchp = newx; 424 if (newx > *(*lnp)->lastchp) 425 *(*lnp)->lastchp = newx; 426 __touchline(win, *y, *x, *x); 427 return OK; 428 } 429 /* check for new line first */ 430 if (char_interp && ((*lnp)->flags & __ISPASTEOL)) { 431 *x = 0; 432 (*lnp)->flags &= ~__ISPASTEOL; 433 if (*y == win->scr_b) { 434 if (!(win->flags & __SCROLLOK)) 435 return ERR; 436 scroll(win); 437 } else { 438 (*y)++; 439 } 440 (*lnp) = win->alines[*y]; 441 lp = &win->alines[*y]->line[*x]; 442 } 443 /* clear out the current character */ 444 cw = (*lp).wcols; 445 if (cw >= 0) { 446 sx = *x; 447 } else { 448 for (sx = *x - 1; sx >= max(*x + cw, 0); sx--) { 449 __CTRACE(__CTRACE_INPUT, 450 "_cursesi_addwchar: clear current char (%d,%d)\n", 451 *y, sx); 452 tp = &win->alines[*y]->line[sx]; 453 tp->ch = win->bch; 454 tp->cflags |= CA_BACKGROUND; 455 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR) 456 return ERR; 457 458 tp->attr = win->battr; 459 tp->wcols = win->wcols; 460 } 461 sx = *x + cw; 462 (*lnp)->flags |= __ISDIRTY; 463 newx = sx + win->ch_off; 464 if (newx < *(*lnp)->firstchp) 465 *(*lnp)->firstchp = newx; 466 } 467 468 /* check for enough space before the end of line */ 469 cw = wcwidth(wch->vals[0]); 470 if (cw < 0) 471 cw = 1; 472 473 if (cw > win->maxx - *x) { 474 __CTRACE(__CTRACE_INPUT, 475 "_cursesi_addwchar: clear EOL (%d,%d)\n", *y, *x); 476 if (*y == win->scr_b) { 477 if (!(win->flags & __SCROLLOK)) 478 return ERR; 479 scroll(win); 480 } 481 482 (*lnp)->flags |= __ISDIRTY; 483 newx = *x + win->ch_off; 484 if (newx < *(*lnp)->firstchp) 485 *(*lnp)->firstchp = newx; 486 for (tp = lp; *x < win->maxx; tp++, (*x)++) { 487 tp->ch = win->bch; 488 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR) 489 return ERR; 490 tp->attr = win->battr; 491 tp->wcols = win->wcols; 492 } 493 newx = win->maxx - 1 + win->ch_off; 494 if (newx > *(*lnp)->lastchp) 495 *(*lnp)->lastchp = newx; 496 __touchline(win, *y, sx, (int) win->maxx - 1); 497 sx = *x = 0; 498 if (*y != win->scr_b) { 499 (*y)++; 500 } 501 lp = &win->alines[*y]->line[0]; 502 (*lnp) = win->alines[*y]; 503 } 504 505 /* add spacing character */ 506 __CTRACE(__CTRACE_INPUT, 507 "_cursesi_addwchar: add character (%d,%d) 0x%x\n", 508 *y, *x, wch->vals[0]); 509 (*lnp)->flags |= __ISDIRTY; 510 newx = *x + win->ch_off; 511 if (newx < *(*lnp)->firstchp) 512 *(*lnp)->firstchp = newx; 513 if (lp->nsp) { 514 __cursesi_free_nsp(lp->nsp); 515 lp->nsp = NULL; 516 } 517 518 lp->ch = wch->vals[0]; 519 lp->cflags &= ~CA_BACKGROUND; 520 521 attributes = (win->wattr | wch->attributes) 522 & (WA_ATTRIBUTES & ~__COLOR); 523 if (wch->attributes & __COLOR) 524 attributes |= wch->attributes & __COLOR; 525 else if (win->wattr & __COLOR) 526 attributes |= win->wattr & __COLOR; 527 if (attributes & __COLOR) 528 lp->attr = attributes | (win->battr & ~__COLOR); 529 else 530 lp->attr = attributes | win->battr; 531 532 lp->wcols = cw; 533 534 __CTRACE(__CTRACE_INPUT, 535 "_cursesi_addwchar: add spacing char 0x%x, attr 0x%x, width %d\n", 536 lp->ch, lp->attr, lp->wcols); 537 538 if (wch->elements > 1) { 539 for (i = 1; i < wch->elements; i++) { 540 np = malloc(sizeof(nschar_t)); 541 if (!np) 542 return ERR; 543 np->ch = wch->vals[i]; 544 np->next = lp->nsp; 545 __CTRACE(__CTRACE_INPUT, 546 "_cursesi_addwchar: add non-spacing char 0x%x\n", 547 np->ch); 548 lp->nsp = np; 549 } 550 } 551 __CTRACE(__CTRACE_INPUT, 552 "_cursesi_addwchar: non-spacing list header: %p\n", lp->nsp); 553 __CTRACE(__CTRACE_INPUT, 554 "_cursesi_addwchar: add rest columns (%d:%d)\n", 555 sx + 1, sx + cw - 1); 556 __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: *x = %d, win->maxx = %d\n", 557 *x, win->maxx); 558 for (tp = lp + 1, *x = sx + 1; *x - sx <= cw - 1; tp++, (*x)++) { 559 if (tp->nsp) { 560 __cursesi_free_nsp(tp->nsp); 561 tp->nsp = NULL; 562 } 563 tp->ch = wch->vals[0]; 564 tp->attr = lp->attr & WA_ATTRIBUTES; 565 /* Mark as "continuation" cell */ 566 tp->cflags |= CA_CONTINUATION; 567 } 568 569 570 if (*x >= win->maxx) { 571 __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: do line wrap\n"); 572 if (*y == win->scr_b) { 573 __CTRACE(__CTRACE_INPUT, 574 "_cursesi_addwchar: at bottom of screen\n"); 575 if (!(win->flags & __SCROLLOK)) 576 return ERR; 577 __CTRACE(__CTRACE_INPUT, 578 "_cursesi_addwchar: do a scroll\n"); 579 scroll(win); 580 } 581 newx = win->maxx - 1 + win->ch_off; 582 if (newx > *(*lnp)->lastchp) 583 *(*lnp)->lastchp = newx; 584 __touchline(win, *y, sx, (int) win->maxx - 1); 585 *x = sx = 0; 586 if (*y != win->scr_b) { 587 (*y)++; 588 } 589 lp = &win->alines[*y]->line[0]; 590 (*lnp) = win->alines[*y]; 591 *(*lnp)->lastchp = win->ch_off + win->maxx - 1; 592 } else { 593 594 /* clear the remaining of the current character */ 595 if (*x && *x < win->maxx) { 596 ex = sx + cw; 597 tp = &win->alines[*y]->line[ex]; 598 while (ex < win->maxx && tp->wcols < 0) { 599 __CTRACE(__CTRACE_INPUT, 600 "_cursesi_addwchar: clear " 601 "remaining of current char (%d,%d)nn", 602 *y, ex); 603 tp->ch = win->bch; 604 tp->cflags |= CA_BACKGROUND; 605 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR) 606 return ERR; 607 tp->attr = win->battr; 608 tp->wcols = win->wcols; 609 tp++, ex++; 610 } 611 newx = ex - 1 + win->ch_off; 612 if (newx > *(*lnp)->lastchp) 613 *(*lnp)->lastchp = newx; 614 __touchline(win, *y, sx, ex - 1); 615 } 616 } 617 618 __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: %d : 0x%x\n", 619 lp->ch, lp->attr); 620 __CTRACE(__CTRACE_INPUT, 621 "_cursesi_addwchar: *x = %d, *y = %d, win->maxx = %d\n", 622 *x, *y, win->maxx); 623 __sync(win); 624 return OK; 625 #endif 626 } 627