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