1 /* $NetBSD: addbytes.c,v 1.40 2013/10/16 19:59:29 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.40 2013/10/16 19:59:29 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 return __waddbytes(stdscr, bytes, count, 0); 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 __waddbytes(win, bytes, count, 0); 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 __waddbytes(win, bytes, count, 0); 97 } 98 99 #endif 100 101 /* 102 * waddbytes -- 103 * Add the character to the current position in the given window. 104 */ 105 int 106 __waddbytes(WINDOW *win, const char *bytes, int count, attr_t attr) 107 { 108 int x, y, err; 109 __LINE *lp; 110 #ifdef HAVE_WCHAR 111 int n; 112 cchar_t cc; 113 wchar_t wc; 114 mbstate_t st; 115 #else 116 int c; 117 #endif 118 #ifdef DEBUG 119 int i; 120 121 for (i = 0; i < win->maxy; i++) { 122 assert(win->alines[i]->sentinel == SENTINEL_VALUE); 123 } 124 125 __CTRACE(__CTRACE_INPUT, "ADDBYTES: add %d bytes\n", count); 126 #endif 127 128 err = OK; 129 SYNCH_IN; 130 lp = win->alines[y]; 131 132 #ifdef HAVE_WCHAR 133 (void)memset(&st, 0, sizeof(st)); 134 #endif 135 while (count > 0) { 136 #ifndef HAVE_WCHAR 137 c = *bytes++; 138 #ifdef DEBUG 139 __CTRACE(__CTRACE_INPUT, "ADDBYTES('%c', %x) at (%d, %d)\n", 140 c, attr, y, x); 141 #endif 142 err = _cursesi_addbyte(win, &lp, &y, &x, c, attr); 143 count--; 144 #else 145 /* 146 * For wide-character support only, try and convert the 147 * given string into a wide character - we do this because 148 * this is how ncurses behaves (not that I think this is 149 * actually the correct thing to do but if we don't do it 150 * a lot of things that rely on this behaviour will break 151 * and we will be blamed). If the conversion succeeds 152 * then we eat the n characters used to make the wide char 153 * from the string. 154 */ 155 n = (int)mbrtowc(&wc, bytes, (size_t)count, &st); 156 if (n < 0) { 157 /* not a valid conversion just eat a char */ 158 wc = *bytes; 159 n = 1; 160 (void)memset(&st, 0, sizeof(st)); 161 } else if (wc == 0) { 162 break; 163 } 164 #ifdef DEBUG 165 __CTRACE(__CTRACE_INPUT, 166 "ADDBYTES WIDE(0x%x [%s], %x) at (%d, %d), ate %d bytes\n", 167 (unsigned) wc, unctrl((unsigned) wc), attr, y, x, n); 168 #endif 169 cc.vals[0] = wc; 170 cc.elements = 1; 171 cc.attributes = attr; 172 err = _cursesi_addwchar(win, &lp, &y, &x, &cc); 173 bytes += n; 174 count -= n; 175 #endif 176 } 177 178 SYNCH_OUT; 179 180 #ifdef DEBUG 181 for (i = 0; i < win->maxy; i++) { 182 assert(win->alines[i]->sentinel == SENTINEL_VALUE); 183 } 184 #endif 185 186 return (err); 187 } 188 189 /* 190 * _cursesi_addbyte - 191 * Internal function to add a byte and update the row and column 192 * positions as appropriate. This function is only used in the narrow 193 * character version of curses. 194 */ 195 int 196 _cursesi_addbyte(WINDOW *win, __LINE **lp, int *y, int *x, int c, 197 attr_t attr) 198 { 199 static char blanks[] = " "; 200 int newx; 201 attr_t attributes; 202 int tabsize; 203 204 switch (c) { 205 case '\t': 206 tabsize = win->screen->TABSIZE; 207 PSYNCH_OUT; 208 if (waddbytes(win, blanks, tabsize - (*x % tabsize)) == ERR) 209 return (ERR); 210 PSYNCH_IN; 211 break; 212 213 default: 214 #ifdef DEBUG 215 __CTRACE(__CTRACE_INPUT, "ADDBYTES(%p, %d, %d)\n", 216 win, *y, *x); 217 #endif 218 219 if ((*lp)->flags & __ISPASTEOL) { 220 new_line: 221 *x = 0; 222 (*lp)->flags &= ~__ISPASTEOL; 223 if (*y == win->scr_b) { 224 #ifdef DEBUG 225 __CTRACE(__CTRACE_INPUT, 226 "ADDBYTES - on bottom " 227 "of scrolling region\n"); 228 #endif 229 if (!(win->flags & __SCROLLOK)) 230 return ERR; 231 PSYNCH_OUT; 232 scroll(win); 233 PSYNCH_IN; 234 } else { 235 (*y)++; 236 } 237 *lp = win->alines[*y]; 238 if (c == '\n') 239 break; 240 } 241 242 attributes = (win->wattr | attr) & 243 (__ATTRIBUTES & ~__COLOR); 244 if (attr & __COLOR) 245 attributes |= attr & __COLOR; 246 else if (win->wattr & __COLOR) 247 attributes |= win->wattr & __COLOR; 248 #ifdef DEBUG 249 __CTRACE(__CTRACE_INPUT, 250 "ADDBYTES: 1: y = %d, x = %d, firstch = %d, " 251 "lastch = %d\n", 252 *y, *x, *win->alines[*y]->firstchp, 253 *win->alines[*y]->lastchp); 254 #endif 255 /* 256 * Always update the change pointers. Otherwise, 257 * we could end up not displaying 'blank' characters 258 * when overlapping windows are displayed. 259 */ 260 newx = *x + win->ch_off; 261 (*lp)->flags |= __ISDIRTY; 262 /* 263 * firstchp/lastchp are shared between 264 * parent window and sub-window. 265 */ 266 if (newx < *(*lp)->firstchp) 267 *(*lp)->firstchp = newx; 268 if (newx > *(*lp)->lastchp) 269 *(*lp)->lastchp = newx; 270 #ifdef DEBUG 271 __CTRACE(__CTRACE_INPUT, 272 "ADDBYTES: change gives f/l: %d/%d [%d/%d]\n", 273 *(*lp)->firstchp, *(*lp)->lastchp, 274 *(*lp)->firstchp - win->ch_off, 275 *(*lp)->lastchp - win->ch_off); 276 #endif 277 if (win->bch != ' ' && c == ' ') 278 (*lp)->line[*x].ch = win->bch; 279 else 280 (*lp)->line[*x].ch = c; 281 282 if (attributes & __COLOR) 283 (*lp)->line[*x].attr = 284 attributes | (win->battr & ~__COLOR); 285 else 286 (*lp)->line[*x].attr = attributes | win->battr; 287 288 if (*x == win->maxx - 1) 289 (*lp)->flags |= __ISPASTEOL; 290 else 291 (*x)++; 292 #ifdef DEBUG 293 __CTRACE(__CTRACE_INPUT, 294 "ADDBYTES: 2: y = %d, x = %d, firstch = %d, " 295 "lastch = %d\n", 296 *y, *x, *win->alines[*y]->firstchp, 297 *win->alines[*y]->lastchp); 298 #endif 299 break; 300 case '\n': 301 PSYNCH_OUT; 302 wclrtoeol(win); 303 PSYNCH_IN; 304 goto new_line; 305 case '\r': 306 *x = 0; 307 break; 308 case '\b': 309 if (--(*x) < 0) 310 *x = 0; 311 break; 312 } 313 314 return (OK); 315 } 316 317 /* 318 * _cursesi_addwchar - 319 * Internal function to add a wide character and update the row 320 * and column positions. 321 */ 322 int 323 _cursesi_addwchar(WINDOW *win, __LINE **lnp, int *y, int *x, 324 const cchar_t *wch) 325 { 326 #ifndef HAVE_WCHAR 327 return (ERR); 328 #else 329 int sx = 0, ex = 0, cw = 0, i = 0, newx = 0, tabsize; 330 __LDATA *lp = &win->alines[*y]->line[*x], *tp = NULL; 331 nschar_t *np = NULL; 332 cchar_t cc; 333 attr_t attributes; 334 335 /* special characters handling */ 336 switch (wch->vals[0]) { 337 case L'\b': 338 if (--*x < 0) 339 *x = 0; 340 win->curx = *x; 341 return OK; 342 case L'\r': 343 *x = 0; 344 return OK; 345 case L'\n': 346 wclrtoeol(win); 347 PSYNCH_IN; 348 *x = 0; 349 (*lnp)->flags &= ~__ISPASTEOL; 350 if (*y == win->scr_b) { 351 if (!(win->flags & __SCROLLOK)) 352 return ERR; 353 PSYNCH_OUT; 354 scroll(win); 355 PSYNCH_IN; 356 } else { 357 (*y)++; 358 } 359 PSYNCH_OUT; 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 for (i = 0; i < tabsize - (*x % tabsize); i++) { 367 if (wadd_wch(win, &cc) == ERR) 368 return ERR; 369 } 370 return OK; 371 } 372 373 /* check for non-spacing character */ 374 if (!wcwidth(wch->vals[0])) { 375 #ifdef DEBUG 376 __CTRACE(__CTRACE_INPUT, 377 "_cursesi_addwchar: char '%c' is non-spacing\n", 378 wch->vals[0]); 379 #endif /* DEBUG */ 380 cw = WCOL(*lp); 381 if (cw < 0) { 382 lp += cw; 383 *x += cw; 384 } 385 for (i = 0; i < wch->elements; i++) { 386 if (!(np = (nschar_t *) malloc(sizeof(nschar_t)))) 387 return ERR;; 388 np->ch = wch->vals[i]; 389 np->next = lp->nsp; 390 lp->nsp = np; 391 } 392 (*lnp)->flags |= __ISDIRTY; 393 newx = *x + win->ch_off; 394 if (newx < *(*lnp)->firstchp) 395 *(*lnp)->firstchp = newx; 396 if (newx > *(*lnp)->lastchp) 397 *(*lnp)->lastchp = newx; 398 __touchline(win, *y, *x, *x); 399 return OK; 400 } 401 /* check for new line first */ 402 if ((*lnp)->flags & __ISPASTEOL) { 403 *x = 0; 404 (*lnp)->flags &= ~__ISPASTEOL; 405 if (*y == win->scr_b) { 406 if (!(win->flags & __SCROLLOK)) 407 return ERR; 408 PSYNCH_OUT; 409 scroll(win); 410 PSYNCH_IN; 411 } else { 412 (*y)++; 413 } 414 (*lnp) = win->alines[*y]; 415 lp = &win->alines[*y]->line[*x]; 416 } 417 /* clear out the current character */ 418 cw = WCOL(*lp); 419 if (cw >= 0) { 420 sx = *x; 421 } else { 422 for (sx = *x - 1; sx >= max(*x + cw, 0); sx--) { 423 #ifdef DEBUG 424 __CTRACE(__CTRACE_INPUT, 425 "_cursesi_addwchar: clear current char (%d,%d)\n", 426 *y, sx); 427 #endif /* DEBUG */ 428 tp = &win->alines[*y]->line[sx]; 429 tp->ch = (wchar_t) btowc((int) win->bch); 430 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR) 431 return ERR; 432 433 tp->attr = win->battr; 434 SET_WCOL(*tp, 1); 435 } 436 sx = *x + cw; 437 (*lnp)->flags |= __ISDIRTY; 438 newx = sx + win->ch_off; 439 if (newx < *(*lnp)->firstchp) 440 *(*lnp)->firstchp = newx; 441 } 442 443 /* check for enough space before the end of line */ 444 cw = wcwidth(wch->vals[0]); 445 if (cw < 0) 446 cw = 1; 447 if (cw > win->maxx - *x) { 448 #ifdef DEBUG 449 __CTRACE(__CTRACE_INPUT, 450 "_cursesi_addwchar: clear EOL (%d,%d)\n", 451 *y, *x); 452 #endif /* DEBUG */ 453 (*lnp)->flags |= __ISDIRTY; 454 newx = *x + win->ch_off; 455 if (newx < *(*lnp)->firstchp) 456 *(*lnp)->firstchp = newx; 457 for (tp = lp; *x < win->maxx; tp++, (*x)++) { 458 tp->ch = (wchar_t) btowc((int) win->bch); 459 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR) 460 return ERR; 461 tp->attr = win->battr; 462 SET_WCOL(*tp, 1); 463 } 464 newx = win->maxx - 1 + win->ch_off; 465 if (newx > *(*lnp)->lastchp) 466 *(*lnp)->lastchp = newx; 467 __touchline(win, *y, sx, (int) win->maxx - 1); 468 sx = *x = 0; 469 if (*y == win->scr_b) { 470 if (!(win->flags & __SCROLLOK)) 471 return ERR; 472 PSYNCH_OUT; 473 scroll(win); 474 PSYNCH_IN; 475 } else { 476 (*y)++; 477 } 478 lp = &win->alines[*y]->line[0]; 479 (*lnp) = win->alines[*y]; 480 } 481 win->cury = *y; 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 = (nschar_t *)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 #endif /* DEBUG */ 539 for (tp = lp + 1, *x = sx + 1; *x - sx <= cw - 1; tp++, (*x)++) { 540 if (tp->nsp) { 541 __cursesi_free_nsp(tp->nsp); 542 tp->nsp = NULL; 543 } 544 tp->ch = wch->vals[0]; 545 tp->attr = lp->attr & WA_ATTRIBUTES; 546 /* Mark as "continuation" cell */ 547 tp->attr |= __WCWIDTH; 548 } 549 if (*x == win->maxx) { 550 (*lnp)->flags |= __ISPASTEOL; 551 newx = win->maxx - 1 + win->ch_off; 552 if (newx > *(*lnp)->lastchp) 553 *(*lnp)->lastchp = newx; 554 __touchline(win, *y, sx, (int) win->maxx - 1); 555 win->curx = sx; 556 } else { 557 win->curx = *x; 558 559 /* clear the remining of the current characer */ 560 if (*x && *x < win->maxx) { 561 ex = sx + cw; 562 tp = &win->alines[*y]->line[ex]; 563 while (ex < win->maxx && WCOL(*tp) < 0) { 564 #ifdef DEBUG 565 __CTRACE(__CTRACE_INPUT, 566 "_cursesi_addwchar: clear " 567 "remaining of current char (%d,%d)nn", 568 *y, ex); 569 #endif /* DEBUG */ 570 tp->ch = (wchar_t) btowc((int) win->bch); 571 if (_cursesi_copy_nsp(win->bnsp, tp) == ERR) 572 return ERR; 573 tp->attr = win->battr; 574 SET_WCOL(*tp, 1); 575 tp++, ex++; 576 } 577 newx = ex - 1 + win->ch_off; 578 if (newx > *(*lnp)->lastchp) 579 *(*lnp)->lastchp = newx; 580 __touchline(win, *y, sx, ex - 1); 581 } 582 } 583 584 #ifdef DEBUG 585 __CTRACE(__CTRACE_INPUT, "add_wch: %d : 0x%x\n", lp->ch, lp->attr); 586 #endif /* DEBUG */ 587 return OK; 588 #endif 589 } 590