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