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