1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* LINTLIBRARY */ 29 30 /* 31 * m_cc.c 32 * 33 * XCurses Library 34 * 35 * Copyright 1990, 1995 by Mortice Kern Systems Inc. All rights reserved. 36 * 37 */ 38 39 #if M_RCSID 40 #ifndef lint 41 static char rcsID[] = 42 "$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/" 43 "libxcurses/src/libc/xcurses/rcs/m_cc.c 1.40 1998/06/12 12:45:39 " 44 "cbates Exp $"; 45 #endif 46 #endif 47 48 #include <private.h> 49 #include <limits.h> 50 #include <m_wio.h> 51 #include <string.h> 52 53 typedef struct { 54 int max; 55 int used; 56 char *mbs; 57 } t_string; 58 59 static int 60 write_string(int byte, t_string *sp) 61 { 62 if (sp->max <= sp->used) 63 return (EOF); 64 65 sp->mbs[sp->used++] = (char)byte; 66 67 return (byte); 68 } 69 70 /* 71 * Convert a wint_t string into a multibyte string. 72 * 73 * The conversion stops at the end of string or the first WEOF. 74 * Return the number of bytes successfully placed into mbs. 75 */ 76 int 77 wistombs(char *mbs, const wint_t *wis, int n) 78 { 79 int last; 80 t_string string = { 0 }; 81 t_wide_io convert = { 0 }; 82 83 string.max = n; 84 string.mbs = mbs; 85 convert.object = (void *) &string; 86 convert.put = (int (*)(int, void *)) write_string; 87 88 for (; ; ++wis) { 89 /* In case of error, rewind string to the last character. */ 90 last = string.used; 91 92 if (m_wio_put(*wis, &convert) < 0) { 93 string.used = last; 94 break; 95 } 96 97 /* 98 * Test for end of string AFTER trying to copy into the 99 * buffer, because m_wio_put() has to handle state changes 100 * back to the initial state on '\0' or WEOF. 101 */ 102 if (*wis == '\0' || *wis == WEOF) 103 break; 104 } 105 106 /* 107 * m_wio_put() does not write '\0', because the "stream" 108 * object is considered to be in "text" mode, which in the 109 * case of file I/O produces undefined results for systems 110 * using locking-shift character sets. 111 */ 112 string.mbs[string.used] = '\0'; 113 114 return (string.used); 115 } 116 117 /* 118 * Convert a wint_t string (filled in by wgetn_wstr()) to a wchar_t string. 119 * The conversion stops at the end of string or the first WEOF. Return the 120 * number of successfully copied characters. 121 * 122 * This routinue should be used when sizeof (wchar_t) < sizeof (wint_t). 123 */ 124 int 125 wistowcs(wchar_t *wcs, const wint_t *wis, int n) 126 { 127 wchar_t *start; 128 129 if (n < 0) 130 n = INT_MAX; 131 132 for (start = wcs; *wis != '\0' && 0 < n; ++wis, ++wcs, --n) { 133 if (*wis == WEOF) 134 break; 135 *wcs = (wchar_t)*wis; 136 } 137 *wcs = '\0'; 138 139 /* (wcs - start) should be enough small to fit in "int" */ 140 return ((int)(wcs - start)); 141 } 142 143 void 144 __m_touch_locs(WINDOW *w, int row, int firstCol, int lastCol) 145 { 146 if (w) { 147 if (firstCol < w->_first[row]) 148 w->_first[row] = (short)firstCol; 149 if (lastCol > w->_last[row]) 150 w->_last[row] = (short)lastCol; 151 } 152 } 153 154 /* 155 * Convert a chtype to a cchar_t. 156 */ 157 int 158 __m_chtype_cc(chtype ch, cchar_t *cc) 159 { 160 char mb; 161 162 cc->_f = 1; 163 cc->_n = 1; 164 mb = (char)(ch & A_CHARTEXT); 165 166 cc->_co = (short)PAIR_NUMBER((int)ch); 167 cc->_at = (attr_t)((ch & (A_ATTRIBUTES & ~A_COLOR)) >> 16); 168 169 if (mb == 0) 170 cc->_wc[0] = cc->_wc[1] = 0; 171 else if (mbtowc(cc->_wc, &mb, 1) < 0) { 172 return (ERR); 173 } 174 return (OK); 175 } 176 177 /* 178 * Return a complex character as a chtype. 179 */ 180 chtype 181 __m_cc_chtype(const cchar_t *cc) 182 { 183 chtype ch; 184 unsigned char mb[MB_LEN_MAX]; 185 186 /* Is it a single-byte character? */ 187 if (cc->_n != 1 || wctomb((char *)mb, cc->_wc[0]) != 1) 188 return ((chtype) ERR); 189 190 ch = ((chtype) cc->_at << 16) & ~A_COLOR; 191 ch |= COLOR_PAIR(cc->_co) | mb[0]; 192 193 return (ch); 194 } 195 196 /* 197 * Convert a complex character's "character" into a multibyte string. 198 * The attribute and colour are ignored. 199 * 200 * If 0 < n, set a new multibyte string and convert the first character, 201 * returning either -1 on error or the number of bytes used to convert the 202 * character. 203 * 204 * If n == 0, continue appending to the current multibyte string and return 205 * a value as for 0 < n case. 206 * 207 * If n < 0, return the accumulated byte length of the current multibyte 208 * string and do nothing else. 209 * 210 * When converting a character, a null cchar_t pointer will force the initial 211 * shift state and append a '\0' to the multibyte string. The return value 212 * will instead by the number of bytes used to shift to the initial state, 213 * and exclude the '\0'. 214 */ 215 int 216 __m_cc_mbs(const cchar_t *cc, char *mbs, int n) 217 { 218 int i, bytes, count, last; 219 static t_string string = { 0 }; 220 static t_wide_io convert = { 0 }; 221 222 if (n < 0) { 223 /* Return total number of bytes written to multibyte string. */ 224 return (string.used); 225 } else if (0 < n) { 226 /* Start a new conversion. */ 227 string.max = n; 228 string.used = 0; 229 string.mbs = mbs; 230 231 convert._next = convert._size = 0; 232 convert.object = (void *) &string; 233 convert.put = (int (*)(int, void *)) write_string; 234 } /* else n == 0, continue appending to previous mbs. */ 235 236 /* In case of error, rewind string to the last character. */ 237 last = string.used; 238 239 if (cc == NULL) { 240 /* Force initial shift state. */ 241 if ((count = m_wio_put('\0', &convert)) < 0) { 242 string.used = last; 243 return (-1); 244 } 245 246 if (string.used < string.max) 247 string.mbs[string.used++] = '\0'; 248 } else { 249 for (count = i = 0; i < cc->_n; ++i, count += bytes) 250 if ((bytes = m_wio_put(cc->_wc[i], &convert)) < 0) { 251 string.used = last; 252 return (-1); 253 } 254 } 255 256 return (count); 257 } 258 259 /* 260 * Convert a stty character into a wchar_t. 261 */ 262 int 263 __m_tty_wc(int index, wchar_t *wcp) 264 { 265 char mb; 266 int code; 267 268 /* 269 * Refer to _shell instead of _prog, since _shell will 270 * correctly reflect the user's prefered settings, whereas 271 * _prog may not have been initialised if both input and 272 * output have been redirected. 273 */ 274 mb = (char)PTERMIOS(_shell)->c_cc[index]; 275 if (mb) 276 code = mbtowc(wcp, &mb, 1) < 0 ? ERR : OK; 277 else 278 code = ERR; 279 280 return (code); 281 } 282 283 /* 284 * Build a cchar_t from the leading spacing and non-spacing characters 285 * in the multibyte character string. Only one spacing character is copied 286 * from the multibyte character string. 287 * 288 * Return the number of characters copied from the string, or -1 on error. 289 */ 290 int 291 __m_mbs_cc(const char *mbs, attr_t at, short co, cchar_t *cc) 292 { 293 wchar_t wc; 294 const char *start; 295 int i, nbytes, width, have_one; 296 297 for (start = mbs, have_one = i = 0; *mbs != '\0'; mbs += nbytes, ++i) { 298 if (sizeof (cc->_wc) <= i) 299 /* Too many characters. */ 300 return (-1); 301 302 if ((nbytes = mbtowc(&wc, mbs, UINT_MAX)) < 0) 303 /* Invalid multibyte sequence. */ 304 return (-1); 305 306 if (nbytes == 0) 307 /* Remainder of string evaluates to the null byte. */ 308 break; 309 310 if (iscntrl(*mbs)) 311 /* Treat control codes like a spacing character. */ 312 width = 1; 313 else 314 width = wcwidth(wc); 315 316 /* Do we have a spacing character? */ 317 if (0 < width) { 318 if (have_one) 319 break; 320 have_one = 1; 321 } 322 323 cc->_wc[i] = wc; 324 } 325 326 cc->_f = 1; 327 cc->_n = (short)i; 328 cc->_co = co; 329 cc->_at = at; 330 331 (void) __m_cc_sort(cc); 332 333 /* (mbs - start) should be enough small to fit in "int" */ 334 return ((int)(mbs - start)); 335 } 336 337 /* 338 * Build a cchar_t from the leading spacing and non-spacing characters 339 * in the wide character string. Only one spacinig character is copied 340 * from the wide character string. 341 * 342 * Return the number of characters copied from the string, or -1 on error. 343 */ 344 int 345 __m_wcs_cc(const wchar_t *wcs, attr_t at, short co, cchar_t *cc) 346 { 347 short i; 348 const wchar_t *start; 349 350 for (start = wcs, i = 0; *wcs != '\0'; ++wcs, ++i) { 351 if (sizeof (cc->_wc) <= i) { 352 /* Too many characters. */ 353 return (-1); 354 } 355 356 if (wcwidth(*wcs) > 0) { 357 if (i != 0) 358 break; 359 } else if ((*wcs == L'\n') || (*wcs == L'\t') || 360 (*wcs == L'\b') || (*wcs == L'\r')) { 361 if (i != 0) 362 break; 363 cc->_wc[i++] = *wcs++; 364 break; 365 } 366 367 cc->_wc[i] = *wcs; 368 } 369 370 cc->_f = 1; 371 cc->_n = i; 372 cc->_co = co; 373 cc->_at = at; 374 375 /* (wcs - start) should be enough small to fit in "int" */ 376 return ((int)(wcs - start)); 377 } 378 379 /* 380 * Convert a single wide character into a complex character. 381 */ 382 int 383 __m_wc_cc(wint_t wc, cchar_t *cc) 384 { 385 wchar_t wcs[2]; 386 387 if (wc == WEOF) 388 return (-1); 389 390 if (wc == 0) { 391 /* 392 * converting a null character to a complex character. 393 * __m_wcs_cc assumes that the string is empty, so 394 * just do it here. 395 */ 396 cc->_f = 1; 397 cc->_n = 1; 398 cc->_co = 0; 399 cc->_at = WA_NORMAL; 400 cc->_wc[0] = 0; 401 cc->_wc[1] = 0; 402 } else { 403 /* A real character */ 404 wcs[0] = (wchar_t)wc; 405 wcs[1] = '\0'; 406 (void) __m_wcs_cc(wcs, WA_NORMAL, 0, cc); 407 } 408 409 return (0); 410 } 411 412 /* 413 * Sort a complex character into a spacing character followed 414 * by any non-spacing characters in increasing order of oridinal 415 * values. This facilitates both comparision and writting of 416 * complex characters. More than one spacing character is 417 * considered an error. 418 * 419 * Return the spacing character's column width or -1 if more 420 * than one spacing character appears in cc. 421 */ 422 int 423 __m_cc_sort(cchar_t *cc) 424 { 425 wchar_t wc; 426 int width, i, j, spacing; 427 428 /* Find spacing character and place in as first element. */ 429 for (width = spacing = i = 0; i < cc->_n; ++i) { 430 j = wcwidth(cc->_wc[i]); 431 if (0 < j) { 432 /* More than one spacing character is an error. */ 433 if (0 < width) 434 return (-1); 435 436 wc = cc->_wc[0]; 437 cc->_wc[0] = cc->_wc[i]; 438 cc->_wc[i] = wc; 439 440 spacing = 1; 441 width = j; 442 break; 443 } 444 } 445 446 /* Bubble sort small array. */ 447 for (i = spacing; i < cc->_n; ++i) { 448 for (j = cc->_n - 1; i < j; --j) { 449 if (cc->_wc[j-1] > cc->_wc[j]) { 450 wc = cc->_wc[j]; 451 cc->_wc[j] = cc->_wc[j-1]; 452 cc->_wc[j-1] = wc; 453 } 454 } 455 } 456 457 return (width); 458 } 459 460 /* 461 * Return the first column of a multi-column character, in window. 462 */ 463 int 464 __m_cc_first(WINDOW *w, int y, int x) 465 { 466 cchar_t *lp; 467 468 for (lp = w->_line[y]; 0 < x; --x) { 469 if (lp[x]._f) 470 break; 471 } 472 473 return (x); 474 } 475 476 /* 477 * Return the start of the next multi-column character, in window. 478 */ 479 int 480 __m_cc_next(WINDOW *w, int y, int x) 481 { 482 cchar_t *lp; 483 484 for (lp = w->_line[y]; ++x < w->_maxx; ) { 485 if (lp[x]._f) 486 break; 487 } 488 489 return (x); 490 } 491 492 /* 493 * Return true if valid last column of a multi-column character. 494 */ 495 int 496 __m_cc_islast(WINDOW *w, int y, int x) 497 { 498 int first, width; 499 500 first = __m_cc_first(w, y, x); 501 width = __m_cc_width(&w->_line[y][x]); 502 503 return ((first + width) == (x + 1)); 504 } 505 506 /* 507 * Replace the character at the current cursor location 508 * according to the column width of the character. The 509 * cursor does not advance. 510 * 511 * Return -1 if the character won't fit on the line and the background 512 * was written in its place; else return the width of the character in 513 * screen columns. 514 */ 515 /* ARGSUSED */ 516 int 517 __m_cc_replace(WINDOW *w, int y, int x, 518 const cchar_t *cc, int as_is) 519 { 520 int i, width; 521 cchar_t *cp, *np; 522 523 width = __m_cc_width(cc); 524 525 if (width <= 0) 526 return (__m_cc_modify(w, y, x, cc)); 527 528 /* 529 * If we try to write a broad character that would exceed the 530 * right margin, then write the background character instead. 531 */ 532 if (0 < width && w->_maxx < x + width) { 533 (void) __m_cc_erase(w, y, x, y, w->_maxx-1); 534 return (-1); 535 } 536 537 /* 538 * Erase the region to be occupied by the new character. 539 * __m_cc_erase() will erase whole characters so that 540 * writing a multicolumn character that overwrites the 541 * trailing and leading portions of two already existing 542 * multicolumn characters, erases the remaining portions. 543 */ 544 (void) __m_cc_erase(w, y, x, y, x + width - 1); 545 546 /* Write the first column of the character. */ 547 cp = &w->_line[y][x++]; 548 if (cc->_wc[0] == L' ') { 549 *cp = w->_bg; 550 cp->_at = cc->_at | w->_fg._at; 551 /* 552 * This method fixes: 553 * /tset/CAPIxcurses/fmvwaddchs/fmvwaddchs1{3} 554 * /tset/CAPIxcurses/fwins_wch/fwins_wch1{5} 555 */ 556 cp->_co = (cc->_co) ? cc->_co : w->_fg._co; 557 } else { 558 if (__m_wacs_cc(cc, cp)) { 559 /* 560 * __m_wacs_cc says ALTCHARSET should be cleared 561 * ... Takes priority 562 */ 563 cp->_at = (cc->_at | w->_fg._at) & ~WA_ALTCHARSET; 564 } else { 565 cp->_at = cc->_at | w->_fg._at; 566 } 567 cp->_co = (cc->_co) ? cc->_co : w->_fg._co; 568 } 569 570 /* Mark this as the first column of the character. */ 571 cp->_f = 1; 572 573 /* Duplicate the character in every column the character occupies. */ 574 for (np = cp + 1, i = 1; i < width; ++i, ++x, ++np) { 575 *np = *cp; 576 np->_f = 0; 577 } 578 579 return (width); 580 } 581 582 int 583 __m_do_scroll(WINDOW *w, int y, int x, int *yp, int *xp) 584 { 585 int code = OK; 586 if (w->_maxx <= x) 587 x = w->_maxx - 1; 588 589 ++y; 590 591 if (y == w->_bottom) { 592 --y; 593 if (w->_flags & W_CAN_SCROLL) { 594 if (wscrl(w, 1) == ERR) 595 return (ERR); 596 x = 0; 597 /* Test suite seems to want this */ 598 w->_flags |= W_FLUSH; 599 } else { 600 #ifdef BREAKS 601 w->_curx = x; /* Cheezy doing it here */ 602 w->_cury = y; 603 #endif /* BREAKS */ 604 code = ERR; /* No scrolling allowed */ 605 } 606 } else if (w->_maxy <= y) { 607 y = w->_maxy - 1; 608 } else { 609 /* 610 * The cursor wraps for any line (in and out of the scroll 611 * region) except for the last line of the scroll region. 612 */ 613 x = 0; 614 } 615 616 *yp = y; 617 *xp = x; 618 619 return (code); 620 } 621 622 /* 623 * Add the character at the current cursor location 624 * according to the column width of the character. 625 * The cursor will be advanced. 626 * Wrapping is done. 627 * 628 * Return ERR if adding the character causes the 629 * screen to scroll, when it is disallowed. 630 */ 631 int 632 __m_cc_add(WINDOW *w, int y, int x, 633 const cchar_t *cc, int as_is, int *yp, int *xp) 634 { 635 int nx, width, code = ERR; 636 637 switch (cc->_wc[0]) { 638 case L'\t': 639 nx = x + (8 - (x & 07)); 640 if (nx >= w->_maxx) { 641 /* This fixes (scroll-disabled) */ 642 /* /tset/CAPIxcurses/fwaddch/fwaddch1{4} but */ 643 /* what does it break? */ 644 nx = w->_maxx; 645 } 646 if (__m_cc_erase(w, y, x, y, nx-1) == -1) 647 goto error; 648 x = nx; 649 650 if (w->_maxx <= x) { 651 if (__m_do_scroll(w, y, x, &y, &x) == ERR) 652 goto error; 653 } 654 break; 655 case L'\n': 656 if (__m_cc_erase(w, y, x, y, w->_maxx-1) == -1) 657 goto error; 658 659 if (__m_do_scroll(w, y, x, &y, &x) == ERR) 660 goto error; 661 break; 662 case L'\r': 663 x = 0; 664 break; 665 case L'\b': 666 if (0 < x) 667 --x; 668 else 669 (void) beep(); 670 break; 671 default: 672 width = __m_cc_replace(w, y, x, cc, as_is); 673 674 x += width; 675 676 if (width < 0 || w->_maxx <= x) { 677 if (__m_do_scroll(w, y, x, &y, &x) == ERR) { 678 goto error; 679 } 680 681 if (width < 0) 682 x += __m_cc_replace(w, y, x, cc, as_is); 683 } 684 } 685 686 code = OK; 687 error: 688 *yp = y; 689 *xp = x; 690 691 return (code); 692 } 693 694 /* 695 * Stripped version of __m_cc_add which does much less special character 696 * processing. Functions such as waddchnstr() are not supposed to do 697 * any special character processing but what does one do when a '\n' 698 * is sent? The test suite expects a new line to start... 699 * 700 * Return ERR if adding the character causes the 701 * screen to scroll, when it is disallowed. 702 */ 703 int 704 __m_cc_add_k(WINDOW *w, int y, int x, 705 const cchar_t *cc, int as_is, int *yp, int *xp) 706 { 707 int width, code = ERR; 708 709 switch (cc->_wc[0]) { 710 case L'\n': 711 if (__m_cc_erase(w, y, x, y, w->_maxx-1) == -1) 712 goto error; 713 714 if (__m_do_scroll(w, y, x, &y, &x) == ERR) 715 goto error; 716 break; 717 default: 718 width = __m_cc_replace(w, y, x, cc, as_is); 719 x += width; 720 } 721 722 code = OK; 723 error: 724 *yp = y; 725 *xp = x; 726 727 return (code); 728 } 729 730 /* 731 * Append non-spacing characters to the a spacing character at (y, x). 732 * Return -1 on error, else 0. 733 */ 734 int 735 __m_cc_modify(WINDOW *w, int y, int x, const cchar_t *cc) 736 { 737 cchar_t *cp, tch; 738 int i, j, width; 739 740 x = __m_cc_first(w, y, x); 741 cp = &w->_line[y][x]; 742 743 /* Is there enough room for the non-spacing characters. */ 744 if (_M_CCHAR_MAX < cp->_n + cc->_n) 745 return (-1); 746 747 for (i = cp->_n, j = 0; j < cc->_n; ++i, ++j) 748 cp->_wc[i] = cc->_wc[j]; 749 cp->_n = (short)i; 750 751 width = __m_cc_width(cp); 752 753 __m_touch_locs(w, y, x, x + width); 754 755 /* Assert that the modified spacing character is sorted. */ 756 (void) __m_cc_sort(cp); 757 758 /* Dulicate in every column occupied by the spacing character. */ 759 while (0 < --width) { 760 tch = *cp; 761 cp[1] = tch; 762 cp++; 763 } 764 765 return (0); 766 } 767 768 static void 769 __m_cc_erase_in_line(WINDOW *w, int y, int x, int lx, int bgWidth) 770 { 771 cchar_t *cp; 772 int i; 773 774 if (x < w->_first[y]) 775 w->_first[y] = (short)x; 776 777 for (cp = w->_line[y], i = 0; x <= lx; ++x, ++i) { 778 cp[x] = w->_bg; 779 /* 780 * The start of each new character will be set true 781 * while internal columns of the character will be 782 * reset to false. 783 */ 784 cp[x]._f = (short)(i % bgWidth == 0); 785 } 786 if (w->_last[y] < x) 787 w->_last[y] = (short)x; 788 } 789 790 /* Window has a parent. Handle width chars overlapping with parent */ 791 static void 792 __m_cc_erase_in_line_sub(WINDOW *w, int y, int x, 793 int lx, int bgWidth, int parentBGWidth) 794 { 795 cchar_t *cp; 796 int i; 797 int xi; 798 int wmin, wmax; 799 int wlx; 800 WINDOW *parent = w->_parent; 801 int parentY = w->_begy + y - parent->_begy; 802 int dx = w->_begx - parent->_begx; 803 804 /* Switch to parent context and calculate limits */ 805 xi = x = __m_cc_first(parent, parentY, dx + x); 806 wlx = lx = __m_cc_next(parent, parentY, dx + lx) - 1; 807 if (wlx >= dx + w->_maxx) wlx = dx + w->_maxx - 1; 808 809 for (cp = parent->_line[parentY]; x <= lx; ) { 810 if ((x < dx) || (x >= (dx + w->_maxx))) { 811 /* Outside target window */ 812 for (i = 0; x <= lx && i <= parentBGWidth; x++, i++) { 813 cp[x] = parent->_bg; 814 cp[x]._f = (i == 0); 815 } 816 } else { 817 /* Inside target window */ 818 for (i = 0; x <= wlx; x++, i++) { 819 cp[x] = w->_bg; 820 cp[x]._f = (short)(i % bgWidth == 0); 821 } 822 } 823 } 824 wmax = x - dx; /* Defaults */ 825 wmin = xi - dx; 826 if ((xi < dx) || (x >= dx + w->_maxx)) { 827 /* Overlaps parent. Must touch parent and child */ 828 int pmin, pmax; 829 830 pmax = dx; /* Defaults */ 831 pmin = dx + w->_maxx; 832 if (xi < dx) { 833 wmin = 0; 834 pmin = xi; 835 } 836 if (x >= dx + w->_maxx) { 837 /* Ends right of target window */ 838 wmax = w->_maxx; 839 pmax = x; 840 } 841 if (pmin < parent->_first[parentY]) 842 parent->_first[parentY] = (short)pmin; 843 if (pmax > parent->_last[parentY]) 844 parent->_last[parentY] = (short)pmax; 845 } 846 if (wmin < w->_first[y]) 847 w->_first[y] = (short)wmin; 848 if (wmax > w->_last[y]) 849 w->_last[y] = (short)wmax; 850 } 851 852 /* 853 * Erase region from (y,x) to (ly, lx) inclusive. The 854 * region is extended left and right in the case where 855 * the portions of a multicolumn characters are erased. 856 * 857 * Return -1 if the region is not an integral multiple 858 * of the background character, else zero for success. 859 */ 860 int 861 __m_cc_erase(WINDOW *w, int y, int x, int ly, int lx) 862 { 863 int bgWidth; 864 865 if (ly < y) 866 return (-1); 867 868 if (w->_maxy <= ly) 869 ly = w->_maxy - 1; 870 871 /* 872 * Is the region to blank out an integral width of the 873 * background character? 874 */ 875 bgWidth = __m_cc_width(&w->_bg); 876 877 if (bgWidth <= 0) 878 return (-1); 879 880 /* 881 * Erase Pattern will look like: 882 * EEEEEEE| 883 * EEEEEEEEEEEEEEE| 884 * EEEEEEEEEEE | 885 */ 886 if (w->_parent) { 887 /* 888 * Use slower alg. for subwindows. 889 * They might erase stuff in parent-context 890 */ 891 int parentBGWidth = __m_cc_width(&w->_parent->_bg); 892 for (; y < ly; ++y, x = 0) { 893 __m_cc_erase_in_line_sub(w, y, x, w->_maxx-1, 894 bgWidth, parentBGWidth); 895 } 896 __m_cc_erase_in_line_sub(w, y, x, lx, bgWidth, parentBGWidth); 897 } else { 898 /* Root windows - no need to work in parent context at all */ 899 if (w->_maxx <= lx) 900 lx = w->_maxx - 1; 901 902 /* 903 * Erase from first whole character (inclusive) to next 904 * character (exclusive). 905 */ 906 x = __m_cc_first(w, y, x); 907 lx = __m_cc_next(w, ly, lx) - 1; 908 909 for (; y < ly; ++y, x = 0) { 910 __m_cc_erase_in_line(w, y, x, w->_maxx-1, bgWidth); 911 } 912 __m_cc_erase_in_line(w, y, x, lx, bgWidth); 913 } 914 return (0); 915 } 916 917 /* 918 * Expand the character to the left or right of the given position. 919 * Return the value returned by __m_cc_replace(). 920 */ 921 int 922 __m_cc_expand(WINDOW *w, int y, int x, int side) 923 { 924 cchar_t cc; 925 int dx, width; 926 927 width = __m_cc_width(&w->_line[y][x]); 928 929 if (side < 0) 930 dx = __m_cc_next(w, y, x) - width; 931 else if (0 < side) 932 dx = __m_cc_first(w, y, x); 933 else 934 return (-1); 935 936 /* 937 * __m_cc_replace() will erase the region containing 938 * the character we want to expand. 939 */ 940 cc = w->_line[y][x]; 941 942 return (__m_cc_replace(w, y, dx, &cc, 0)); 943 } 944 945 /* Revised version of __m_cc_compare() to compare only the char parts */ 946 947 int 948 __m_cc_equal(const cchar_t *c1, const cchar_t *c2) 949 { 950 int i; 951 952 if (c1->_f != c2->_f) 953 return (0); 954 if (c1->_n != c2->_n) 955 return (0); 956 for (i = 0; i < c1->_n; ++i) 957 if (c1->_wc[i] != c2->_wc[i]) 958 return (0); 959 return (1); 960 } 961 962 /* 963 * Return true if characters are equal. 964 * 965 * NOTE to guarantee correct results, make sure that both 966 * characters have been passed through __m_cc_sort(). 967 */ 968 int 969 __m_cc_compare(const cchar_t *c1, const cchar_t *c2, int exact) 970 { 971 int i; 972 973 if (exact && c1->_f != c2->_f) 974 return (0); 975 if (c1->_n != c2->_n) 976 return (0); 977 if ((c1->_at & ~WA_COOKIE) != (c2->_at & ~WA_COOKIE)) 978 return (0); 979 if (c1->_co != c2->_co) 980 return (0); 981 982 for (i = 0; i < c1->_n; ++i) 983 if (c1->_wc[i] != c2->_wc[i]) 984 return (0); 985 986 return (1); 987 } 988 989 /* 990 * Write to the stream the character portion of a cchar_t. 991 */ 992 int 993 __m_cc_write(const cchar_t *cc) 994 { 995 int j; 996 size_t i; 997 char mb[MB_LEN_MAX]; 998 /* 999 * 4131273 UNIX98: xcurses library renders complex characters incorrectly 1000 */ 1001 int backed_up = 0; 1002 1003 for (i = 0; i < cc->_n; ++i) { 1004 j = wctomb(mb, cc->_wc[i]); 1005 if (j == -1) 1006 return (EOF); 1007 if (i == 1) { 1008 /* 1009 * Move cursor back where it was 1010 */ 1011 if (fwrite(cursor_left, 1, strlen(cursor_left), 1012 __m_screen->_of) == 0) { 1013 return (EOF); 1014 } 1015 backed_up = 1; 1016 } 1017 if (fwrite(mb, sizeof (*mb), (size_t)j, __m_screen->_of) == 0) { 1018 return (EOF); 1019 } 1020 } 1021 if (backed_up) { 1022 /* 1023 * Move cursor back where it was 1024 */ 1025 if (fwrite(cursor_right, 1, strlen(cursor_right), 1026 __m_screen->_of) == 0) { 1027 return (EOF); 1028 } 1029 } 1030 1031 __m_screen->_flags |= W_FLUSH; 1032 return (0); 1033 } 1034