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