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