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