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