1 /* $NetBSD: addbytes.c,v 1.47 2017/01/06 14:25:41 roy 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.47 2017/01/06 14:25:41 roy 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;} 51 #define PSYNCH_IN {*y = win->cury; *x = win->curx;} 52 #define PSYNCH_OUT {win->cury = *y; win->curx = *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 (wmove(win, y, x) == 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. This function is only used in the narrow 208 * character version of curses. If update_cursor is non-zero then character 209 * interpretation. 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 return OK; 245 246 case '\b': 247 if (--(*x) < 0) 248 *x = 0; 249 win->curx = *x; 250 return OK; 251 } 252 } 253 254 #ifdef DEBUG 255 __CTRACE(__CTRACE_INPUT, "ADDBYTES(%p, %d, %d)\n", win, *y, *x); 256 #endif 257 258 if (char_interp && ((*lp)->flags & __ISPASTEOL)) { 259 *x = 0; 260 (*lp)->flags &= ~__ISPASTEOL; 261 if (*y == win->scr_b) { 262 #ifdef DEBUG 263 __CTRACE(__CTRACE_INPUT, 264 "ADDBYTES - on bottom " 265 "of scrolling region\n"); 266 #endif 267 if (!(win->flags & __SCROLLOK)) 268 return ERR; 269 PSYNCH_OUT; 270 scroll(win); 271 PSYNCH_IN; 272 } else { 273 (*y)++; 274 } 275 *lp = win->alines[*y]; 276 if (c == '\n') 277 return OK; 278 } 279 280 #ifdef DEBUG 281 __CTRACE(__CTRACE_INPUT, 282 "ADDBYTES: 1: y = %d, x = %d, firstch = %d, lastch = %d\n", 283 *y, *x, *win->alines[*y]->firstchp, 284 *win->alines[*y]->lastchp); 285 #endif 286 287 attributes = (win->wattr | attr) & (__ATTRIBUTES & ~__COLOR); 288 if (attr & __COLOR) 289 attributes |= attr & __COLOR; 290 else if (win->wattr & __COLOR) 291 attributes |= win->wattr & __COLOR; 292 293 /* 294 * Always update the change pointers. Otherwise, 295 * we could end up not displaying 'blank' characters 296 * when overlapping windows are displayed. 297 */ 298 newx = *x + win->ch_off; 299 (*lp)->flags |= __ISDIRTY; 300 /* 301 * firstchp/lastchp are shared between 302 * parent window and sub-window. 303 */ 304 if (newx < *(*lp)->firstchp) 305 *(*lp)->firstchp = newx; 306 if (newx > *(*lp)->lastchp) 307 *(*lp)->lastchp = newx; 308 #ifdef DEBUG 309 __CTRACE(__CTRACE_INPUT, "ADDBYTES: change gives f/l: %d/%d [%d/%d]\n", 310 *(*lp)->firstchp, *(*lp)->lastchp, 311 *(*lp)->firstchp - win->ch_off, 312 *(*lp)->lastchp - win->ch_off); 313 #endif 314 if (win->bch != ' ' && c == ' ') 315 (*lp)->line[*x].ch = win->bch; 316 else 317 (*lp)->line[*x].ch = c; 318 319 if (attributes & __COLOR) 320 (*lp)->line[*x].attr = 321 attributes | (win->battr & ~__COLOR); 322 else 323 (*lp)->line[*x].attr = attributes | win->battr; 324 325 if (*x == win->maxx - 1) 326 (*lp)->flags |= __ISPASTEOL; 327 else 328 (*x)++; 329 330 #ifdef DEBUG 331 __CTRACE(__CTRACE_INPUT, 332 "ADDBYTES: 2: y = %d, x = %d, firstch = %d, lastch = %d\n", 333 *y, *x, *win->alines[*y]->firstchp, 334 *win->alines[*y]->lastchp); 335 #endif 336 __sync(win); 337 return OK; 338 } 339 340 /* 341 * _cursesi_addwchar - 342 * Internal function to add a wide character and update the row 343 * and column positions. 344 */ 345 int 346 _cursesi_addwchar(WINDOW *win, __LINE **lnp, int *y, int *x, 347 const cchar_t *wch, int char_interp) 348 { 349 #ifndef HAVE_WCHAR 350 return ERR; 351 #else 352 int sx = 0, ex = 0, cw = 0, i = 0, newx = 0, tabsize; 353 __LDATA *lp = &win->alines[*y]->line[*x], *tp = NULL; 354 nschar_t *np = NULL; 355 cchar_t cc; 356 attr_t attributes; 357 358 if (char_interp) { 359 /* special characters handling */ 360 switch (wch->vals[0]) { 361 case L'\b': 362 if (--*x < 0) 363 *x = 0; 364 win->curx = *x; 365 return OK; 366 case L'\r': 367 *x = 0; 368 win->curx = *x; 369 return OK; 370 case L'\n': 371 wclrtoeol(win); 372 PSYNCH_IN; 373 *x = 0; 374 (*lnp)->flags &= ~__ISPASTEOL; 375 if (*y == win->scr_b) { 376 if (!(win->flags & __SCROLLOK)) 377 return ERR; 378 PSYNCH_OUT; 379 scroll(win); 380 PSYNCH_IN; 381 } else { 382 (*y)++; 383 } 384 PSYNCH_OUT; 385 return OK; 386 case L'\t': 387 cc.vals[0] = L' '; 388 cc.elements = 1; 389 cc.attributes = win->wattr; 390 tabsize = win->screen->TABSIZE; 391 newx = tabsize - (*x % tabsize); 392 for (i = 0; i < newx; i++) { 393 if (wadd_wch(win, &cc) == ERR) 394 return ERR; 395 (*x)++; 396 } 397 return OK; 398 } 399 } 400 401 /* check for non-spacing character */ 402 if (!wcwidth(wch->vals[0])) { 403 #ifdef DEBUG 404 __CTRACE(__CTRACE_INPUT, 405 "_cursesi_addwchar: char '%c' is non-spacing\n", 406 wch->vals[0]); 407 #endif /* DEBUG */ 408 cw = WCOL(*lp); 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 PSYNCH_OUT; 437 scroll(win); 438 PSYNCH_IN; 439 } else { 440 (*y)++; 441 } 442 (*lnp) = win->alines[*y]; 443 lp = &win->alines[*y]->line[*x]; 444 } 445 /* clear out the current character */ 446 cw = WCOL(*lp); 447 if (cw >= 0) { 448 sx = *x; 449 } else { 450 for (sx = *x - 1; sx >= max(*x + cw, 0); sx--) { 451 #ifdef DEBUG 452 __CTRACE(__CTRACE_INPUT, 453 "_cursesi_addwchar: clear current char (%d,%d)\n", 454 *y, sx); 455 #endif /* DEBUG */ 456 tp = &win->alines[*y]->line[sx]; 457 tp->ch = (wchar_t) btowc((int) win->bch); 458 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR) 459 return ERR; 460 461 tp->attr = win->battr; 462 SET_WCOL(*tp, 1); 463 } 464 sx = *x + cw; 465 (*lnp)->flags |= __ISDIRTY; 466 newx = sx + win->ch_off; 467 if (newx < *(*lnp)->firstchp) 468 *(*lnp)->firstchp = newx; 469 } 470 471 /* check for enough space before the end of line */ 472 cw = wcwidth(wch->vals[0]); 473 if (cw < 0) 474 cw = 1; 475 476 if (cw > win->maxx - *x) { 477 #ifdef DEBUG 478 __CTRACE(__CTRACE_INPUT, 479 "_cursesi_addwchar: clear EOL (%d,%d)\n", 480 *y, *x); 481 #endif /* DEBUG */ 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 = (wchar_t) btowc((int) win->bch); 488 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR) 489 return ERR; 490 tp->attr = win->battr; 491 SET_WCOL(*tp, 1); 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 if (!(win->flags & __SCROLLOK)) 500 return ERR; 501 PSYNCH_OUT; 502 scroll(win); 503 PSYNCH_IN; 504 } else { 505 (*y)++; 506 } 507 lp = &win->alines[*y]->line[0]; 508 (*lnp) = win->alines[*y]; 509 } 510 win->cury = *y; 511 512 /* add spacing character */ 513 #ifdef DEBUG 514 __CTRACE(__CTRACE_INPUT, 515 "_cursesi_addwchar: add character (%d,%d) 0x%x\n", 516 *y, *x, wch->vals[0]); 517 #endif /* DEBUG */ 518 (*lnp)->flags |= __ISDIRTY; 519 newx = *x + win->ch_off; 520 if (newx < *(*lnp)->firstchp) 521 *(*lnp)->firstchp = newx; 522 if (lp->nsp) { 523 __cursesi_free_nsp(lp->nsp); 524 lp->nsp = NULL; 525 } 526 527 lp->ch = wch->vals[0]; 528 529 attributes = (win->wattr | wch->attributes) 530 & (WA_ATTRIBUTES & ~__COLOR); 531 if (wch->attributes & __COLOR) 532 attributes |= wch->attributes & __COLOR; 533 else if (win->wattr & __COLOR) 534 attributes |= win->wattr & __COLOR; 535 if (attributes & __COLOR) 536 lp->attr = attributes | (win->battr & ~__COLOR); 537 else 538 lp->attr = attributes | win->battr; 539 540 SET_WCOL(*lp, cw); 541 542 #ifdef DEBUG 543 __CTRACE(__CTRACE_INPUT, 544 "_cursesi_addwchar: add spacing char 0x%x, attr 0x%x\n", 545 lp->ch, lp->attr); 546 #endif /* DEBUG */ 547 548 if (wch->elements > 1) { 549 for (i = 1; i < wch->elements; i++) { 550 np = malloc(sizeof(nschar_t)); 551 if (!np) 552 return ERR;; 553 np->ch = wch->vals[i]; 554 np->next = lp->nsp; 555 #ifdef DEBUG 556 __CTRACE(__CTRACE_INPUT, 557 "_cursesi_addwchar: add non-spacing char 0x%x\n", np->ch); 558 #endif /* DEBUG */ 559 lp->nsp = np; 560 } 561 } 562 #ifdef DEBUG 563 __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: non-spacing list header: %p\n", 564 lp->nsp); 565 __CTRACE(__CTRACE_INPUT, "_cursesi_addwchar: add rest columns (%d:%d)\n", 566 sx + 1, sx + cw - 1); 567 #endif /* DEBUG */ 568 for (tp = lp + 1, *x = sx + 1; *x - sx <= cw - 1; tp++, (*x)++) { 569 if (tp->nsp) { 570 __cursesi_free_nsp(tp->nsp); 571 tp->nsp = NULL; 572 } 573 tp->ch = wch->vals[0]; 574 tp->attr = lp->attr & WA_ATTRIBUTES; 575 /* Mark as "continuation" cell */ 576 tp->attr |= __WCWIDTH; 577 } 578 579 if (*x == win->maxx) { 580 (*lnp)->flags |= __ISPASTEOL; 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 win->curx = sx; 586 } else { 587 win->curx = *x; 588 589 /* clear the remining of the current characer */ 590 if (*x && *x < win->maxx) { 591 ex = sx + cw; 592 tp = &win->alines[*y]->line[ex]; 593 while (ex < win->maxx && WCOL(*tp) < 0) { 594 #ifdef DEBUG 595 __CTRACE(__CTRACE_INPUT, 596 "_cursesi_addwchar: clear " 597 "remaining of current char (%d,%d)nn", 598 *y, ex); 599 #endif /* DEBUG */ 600 tp->ch = (wchar_t) btowc((int) win->bch); 601 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR) 602 return ERR; 603 tp->attr = win->battr; 604 SET_WCOL(*tp, 1); 605 tp++, ex++; 606 } 607 newx = ex - 1 + win->ch_off; 608 if (newx > *(*lnp)->lastchp) 609 *(*lnp)->lastchp = newx; 610 __touchline(win, *y, sx, ex - 1); 611 } 612 } 613 614 #ifdef DEBUG 615 __CTRACE(__CTRACE_INPUT, "add_wch: %d : 0x%x\n", lp->ch, lp->attr); 616 #endif /* DEBUG */ 617 __sync(win); 618 return OK; 619 #endif 620 } 621