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