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