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