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