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 /* 30 * m_cc.c 31 * 32 * XCurses Library 33 * 34 * Copyright 1990, 1995 by Mortice Kern Systems Inc. All rights reserved. 35 * 36 */ 37 38 #if M_RCSID 39 #ifndef lint 40 static char rcsID[] = "$Header: /rd/src/libc/xcurses/rcs/m_cc.c 1.8 1995/09/20 15:26:52 ant Exp $"; 41 #endif 42 #endif 43 44 #include <private.h> 45 #include <errno.h> 46 #include <limits.h> 47 #include <string.h> 48 #include <m_wio.h> 49 50 typedef struct { 51 int max; 52 int used; 53 char *mbs; 54 } t_string; 55 56 static int 57 write_string(byte, sp) 58 int byte; 59 t_string *sp; 60 { 61 if (sp->max <= sp->used) 62 return EOF; 63 64 sp->mbs[sp->used++] = byte; 65 66 return byte; 67 } 68 69 /* 70 * Convert a wint_t string into a multibyte string. 71 * 72 * The conversion stops at the end of string or the first WEOF. 73 * Return the number of bytes successfully placed into mbs. 74 */ 75 int 76 wistombs(mbs, wis, n) 77 char *mbs; 78 const wint_t *wis; 79 int n; 80 { 81 int last; 82 t_string string = { 0 }; 83 t_wide_io convert = { 0 }; 84 85 string.max = n; 86 string.mbs = mbs; 87 convert.object = (void *) &string; 88 convert.put = (int (*)(int, void *)) write_string; 89 90 for (;; ++wis) { 91 /* In case of error, rewind string to the last character. */ 92 last = string.used; 93 94 if (m_wio_put(*wis, &convert) < 0) { 95 string.used = last; 96 break; 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 /* 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(wcs, wis, n) 126 wchar_t *wcs; 127 const wint_t *wis; 128 int n; 129 { 130 wchar_t *start; 131 132 if (n < 0) 133 n = INT_MAX; 134 135 for (start = wcs; *wis != '\0' && 0 < n; ++wis, ++wcs, --n) { 136 if (*wis == WEOF) 137 break; 138 *wcs = (wchar_t) *wis; 139 } 140 *wcs = '\0'; 141 142 return (int) (wcs - start); 143 } 144 145 /* 146 * Convert a chtype to a cchar_t. 147 */ 148 int 149 __m_chtype_cc(ch, cc) 150 chtype ch; 151 cchar_t *cc; 152 { 153 char mb; 154 155 cc->_f = 1; 156 cc->_n = 1; 157 mb = (char)(ch & A_CHARTEXT); 158 159 if (mbtowc(cc->_wc, &mb, 1) < 0) 160 return ERR; 161 162 cc->_co = (short) PAIR_NUMBER(ch); 163 cc->_at = (attr_t) ((ch & (A_ATTRIBUTES & ~A_COLOR)) >> 16); 164 165 return OK; 166 } 167 168 /* 169 * Return a complex character as a chtype. 170 */ 171 chtype 172 __m_cc_chtype(cc) 173 const cchar_t *cc; 174 { 175 chtype ch; 176 unsigned char mb[MB_LEN_MAX]; 177 178 /* Is it a single-byte character? */ 179 if (cc->_n != 1 || wctomb((char *) mb, cc->_wc[0]) != 1) 180 return (chtype) ERR; 181 182 ch = ((chtype) cc->_at << 16) & ~A_COLOR; 183 ch |= COLOR_PAIR(cc->_co) | mb[0]; 184 185 return ch; 186 } 187 188 /* 189 * Convert a complex character's "character" into a multibyte string. 190 * The attribute and colour are ignored. 191 * 192 * If 0 < n, set a new multibyte string and convert the first character, 193 * returning either -1 on error or the number of bytes used to convert the 194 * character. 195 * 196 * If n == 0, continue appending to the current multibyte string and return 197 * a value as for 0 < n case. 198 * 199 * If n < 0, return the accumulated byte length of the current multibyte 200 * string and do nothing else. 201 * 202 * When converting a character, a null cchar_t pointer will force the initial 203 * shift state and append a '\0' to the multibyte string. The return value 204 * will instead by the number of bytes used to shift to the initial state, 205 * and exclude the '\0'. 206 */ 207 int 208 __m_cc_mbs(cc, mbs, n) 209 const cchar_t *cc; 210 char *mbs; 211 int n; 212 { 213 cchar_t *cp; 214 int i, bytes, count, last; 215 mbstate_t initial = { 0 }; 216 static t_string string = { 0 }; 217 static t_wide_io convert = { 0 }; 218 219 if (n < 0) { 220 /* Return total number of bytes written to multibyte string. */ 221 return string.used; 222 } else if (0 < n) { 223 /* Start a new conversion. */ 224 string.max = n; 225 string.used = 0; 226 string.mbs = mbs; 227 228 convert._state = initial; 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 == (cchar_t *) 0) { 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(index, wcp) 262 int index; 263 wchar_t *wcp; 264 { 265 char mb; 266 int code; 267 268 /* Refer to _shell instead of _prog, since _shell will 269 * correctly reflect the user's prefered settings, whereas 270 * _prog may not have been initialised if both input and 271 * output have been redirected. 272 */ 273 mb = cur_term->_shell.c_cc[index]; 274 code = mbtowc(wcp, &mb, 1) < 0 ? ERR : OK; 275 276 return code; 277 } 278 279 /* 280 * Build a cchar_t from the leading spacing and non-spacing characters 281 * in the multibyte character string. Only one spacing character is copied 282 * from the multibyte character string. 283 * 284 * Return the number of characters copied from the string, or -1 on error. 285 */ 286 int 287 __m_mbs_cc(const char *mbs, attr_t at, short co, cchar_t *cc) 288 { 289 wchar_t wc; 290 const char *start; 291 int i, nbytes, width, have_one; 292 293 for (start = mbs, have_one = i = 0; *mbs != '\0'; mbs += nbytes, ++i) { 294 if (sizeof cc->_wc <= i) 295 /* Too many characters. */ 296 return -1; 297 298 if ((nbytes = mbtowc(&wc, mbs, UINT_MAX)) < 0) 299 /* Invalid multibyte sequence. */ 300 return -1; 301 302 if (nbytes == 0) 303 /* Remainder of string evaluates to the null byte. */ 304 break; 305 306 if (iscntrl(*mbs)) 307 /* Treat control codes like a spacing character. */ 308 width = 1; 309 else if ((width = wcwidth(wc)) < 0) 310 return -1; 311 312 /* Do we have a spacing character? */ 313 if (0 < width) { 314 if (have_one) 315 break; 316 have_one = 1; 317 } 318 319 cc->_wc[i] = wc; 320 } 321 322 cc->_f = 1; 323 cc->_n = i; 324 cc->_co = co; 325 cc->_at = at; 326 327 (void) __m_cc_sort(cc); 328 329 return (int) (mbs - start); 330 } 331 332 /* 333 * Build a cchar_t from the leading spacing and non-spacing characters 334 * in the wide character string. Only one spacinig character is copied 335 * from the wide character string. 336 * 337 * Return the number of characters copied from the string, or -1 on error. 338 */ 339 int 340 __m_wcs_cc(const wchar_t *wcs, attr_t at, short co, cchar_t *cc) 341 { 342 short i; 343 int width, have_one; 344 const wchar_t *start; 345 346 for (start = wcs, have_one = i = 0; *wcs != '\0'; ++wcs, ++i) { 347 if (sizeof cc->_wc <= i) 348 /* Too many characters. */ 349 return -1; 350 351 if ((width = wcwidth(*wcs)) < 0) 352 return -1; 353 354 if (0 < width) { 355 if (have_one) 356 break; 357 have_one = 1; 358 } 359 360 cc->_wc[i] = *wcs; 361 } 362 363 cc->_f = 1; 364 cc->_n = i; 365 cc->_co = co; 366 cc->_at = at; 367 368 (void) __m_cc_sort(cc); 369 370 return (int) (wcs - start); 371 } 372 373 /* 374 * Convert a single wide character into a complex character. 375 */ 376 int 377 __m_wc_cc(wint_t wc, cchar_t *cc) 378 { 379 wchar_t wcs[2]; 380 381 if (wc == WEOF) 382 return -1; 383 384 wcs[0] = (wchar_t)wc; 385 wcs[1] = '\0'; 386 (void) __m_wcs_cc(wcs, WA_NORMAL, 0, cc); 387 388 return 0; 389 } 390 391 /* 392 * Sort a complex character into a spacing character followed 393 * by any non-spacing characters in increasing order of oridinal 394 * values. This facilitates both comparision and writting of 395 * complex characters. More than one spacing character is 396 * considered an error. 397 * 398 * Return the spacing character's column width or -1 if more 399 * than one spacing character appears in cc. 400 */ 401 int 402 __m_cc_sort(cc) 403 cchar_t *cc; 404 { 405 wchar_t wc; 406 int width, i, j, spacing; 407 408 /* Find spacing character and place in as first element. */ 409 for (width = spacing = i = 0; i < cc->_n; ++i) { 410 j = wcwidth(cc->_wc[i]); 411 if (0 < j) { 412 /* More than one spacing character is an error. */ 413 if (0 < width) 414 return -1; 415 416 wc = cc->_wc[0]; 417 cc->_wc[0] = cc->_wc[i]; 418 cc->_wc[i] = wc; 419 420 spacing = 1; 421 width = j; 422 break; 423 } 424 } 425 426 /* Bubble sort small array. */ 427 for (i = spacing; i < cc->_n; ++i) { 428 for (j = cc->_n - 1; i < j; --j) { 429 if (cc->_wc[j-1] > cc->_wc[j]) { 430 wc = cc->_wc[j]; 431 cc->_wc[j] = cc->_wc[j-1]; 432 cc->_wc[j-1] = wc; 433 } 434 } 435 } 436 437 return width; 438 } 439 440 /* 441 * Return width inn screen columns of the character. 442 */ 443 int 444 __m_cc_width(cc) 445 const cchar_t *cc; 446 { 447 return wcwidth(cc->_wc[0]); 448 } 449 450 /* 451 * Return the first column of a multi-column character, in window. 452 */ 453 int 454 __m_cc_first(w, y, x) 455 WINDOW *w; 456 int y, x; 457 { 458 register cchar_t *lp; 459 460 for (lp = w->_line[y]; 0 < x; --x) { 461 if (lp[x]._f) 462 break; 463 } 464 465 return x; 466 } 467 468 /* 469 * Return the start of the next multi-column character, in window. 470 */ 471 int 472 __m_cc_next(w, y, x) 473 WINDOW *w; 474 int y, x; 475 { 476 cchar_t *lp; 477 478 for (lp = w->_line[y]; ++x < w->_maxx; ) { 479 if (lp[x]._f) 480 break; 481 } 482 483 return x; 484 } 485 486 /* 487 * Return true if valid last column of a multi-column character. 488 */ 489 int 490 __m_cc_islast(w, y, x) 491 WINDOW *w; 492 int y, x; 493 { 494 int first, width; 495 496 first = __m_cc_first(w, y, x); 497 width = __m_cc_width(&w->_line[y][x]); 498 499 return first + width == x + 1; 500 } 501 502 /* 503 * Replace the character at the current cursor location 504 * according to the column width of the character. The 505 * cursor does not advance. 506 * 507 * Return -1 if the character won't fit on the line and the background 508 * was written in its place; else return the width of the character in 509 * screen columns. 510 */ 511 int 512 __m_cc_replace(w, y, x, cc, as_is) 513 WINDOW *w; 514 int y, x; 515 const cchar_t *cc; 516 int as_is; 517 { 518 int i, width; 519 cchar_t *cp, *np; 520 521 width = __m_cc_width(cc); 522 523 /* If we try to write a broad character that would exceed the 524 * right margin, then write the background character instead. 525 */ 526 if (0 < width && w->_maxx < x + width) { 527 (void) __m_cc_erase(w, y, x, y, w->_maxx-1); 528 return -1; 529 } 530 531 /* Erase the region to be occupied by the new character. 532 * __m_cc_erase() will erase whole characters so that 533 * writing a multicolumn character that overwrites the 534 * trailing and leading portions of two already existing 535 * multicolumn characters, erases the remaining portions. 536 */ 537 (void) __m_cc_erase(w, y, x, y, x + width - 1); 538 539 /* Write the first column of the character. */ 540 cp = &w->_line[y][x++]; 541 if (cc->_wc[0] == ' ' || cc->_wc[0] == M_MB_L(' ')) { 542 *cp = w->_bg; 543 cp->_at |= cc->_at; 544 if (cc->_co != 0) 545 cp->_co = cc->_co; 546 } else { 547 (void) __m_wacs_cc(cc, cp); 548 if (cc->_co == 0) 549 cp->_co = w->_fg._co; 550 } 551 552 cp->_at |= w->_fg._at | w->_bg._at; 553 554 /* Mark this as the first column of the character. */ 555 cp->_f = 1; 556 557 /* Duplicate the character in every column the character occupies. */ 558 for (np = cp + 1, i = 1; i < width; ++i, ++x, ++np) { 559 *np = *cp; 560 np->_f = 0; 561 } 562 563 return width; 564 } 565 566 int 567 __m_do_scroll(WINDOW *w, int y, int x, int *yp, int *xp) 568 { 569 if (w->_maxx <= x) 570 x = w->_maxx-1; 571 572 ++y; 573 574 if (y == w->_bottom) { 575 --y; 576 if (w->_flags & W_CAN_SCROLL) { 577 if (wscrl(w, 1) == ERR) 578 return ERR; 579 x = 0; 580 } 581 } else if (w->_maxy <= y) { 582 y = w->_maxy-1; 583 } else { 584 /* The cursor wraps for any line (in and out of the scroll 585 * region) except for the last line of the scroll region. 586 */ 587 x = 0; 588 } 589 590 *yp = y; 591 *xp = x; 592 593 return OK; 594 } 595 596 /* 597 * Add the character at the current cursor location 598 * according to the column width of the character. 599 * The cursor will be advanced. 600 * 601 * Return ERR if adding the character causes the 602 * screen to scroll, when it is disallowed. 603 */ 604 int 605 __m_cc_add(w, y, x, cc, as_is, yp, xp) 606 WINDOW *w; 607 int y, x; 608 const cchar_t *cc; 609 int as_is, *yp, *xp; 610 { 611 int nx, width, code = ERR; 612 613 #ifdef M_CURSES_TRACE 614 __m_trace( 615 "__m_cc_add(%p, %d, %d, %p, %d, %p, %p)", 616 w, y, x, cc, as_is, yp, xp 617 ); 618 #endif 619 620 switch (cc->_wc[0]) { 621 case '\t': 622 nx = x + (8 - (x & 07)); 623 if (__m_cc_erase(w, y, x, y, nx-1) == -1) 624 goto error; 625 x = nx; 626 627 if (w->_maxx <= x) { 628 if (__m_do_scroll(w, y, x, &y, &x) == ERR) 629 goto error; 630 } 631 break; 632 case '\n': 633 if (__m_cc_erase(w, y, x, y, w->_maxx-1) == -1) 634 goto error; 635 636 if (__m_do_scroll(w, y, x, &y, &x) == ERR) 637 goto error; 638 break; 639 case '\r': 640 x = 0; 641 break; 642 case '\b': 643 if (0 < x) 644 --x; 645 break; 646 default: 647 width = __m_cc_replace(w, y, x, cc, as_is); 648 649 x += width; 650 651 if (width < 0 || w->_maxx <= x) { 652 if (__m_do_scroll(w, y, x, &y, &x) == ERR) 653 goto error; 654 655 if (width < 0) 656 x += __m_cc_replace(w, y, x, cc, as_is); 657 } 658 } 659 660 code = OK; 661 error: 662 *yp = y; 663 *xp = x; 664 665 return __m_return_code("__m_cc_add", code); 666 } 667 668 /* 669 * Erase region from (y,x) to (ly, lx) inclusive. The 670 * region is extended left and right in the case where 671 * the portions of a multicolumn characters are erased. 672 * 673 * Return -1 if the region is not an integral multiple 674 * of the background character, else zero for success. 675 */ 676 int 677 __m_cc_erase(w, y, x, ly, lx) 678 WINDOW *w; 679 int y, x, ly, lx; 680 { 681 cchar_t *cp; 682 int i, width; 683 684 if (ly < y) 685 return -1; 686 687 if (w->_maxy <= ly) 688 ly = w->_maxy - 1; 689 if (w->_maxx <= lx) 690 lx = w->_maxx - 1; 691 692 /* Erase from first whole character (inclusive) to next 693 * character (exclusive). 694 */ 695 x = __m_cc_first(w, y, x); 696 lx = __m_cc_next(w, ly, lx) - 1; 697 698 /* Is the region to blank out an integral width of the 699 * background character? 700 */ 701 width = __m_cc_width(&w->_bg); 702 703 if (y < ly && (lx + 1) % width != 0) 704 return -1; 705 if ((lx - x + 1) % width != 0) 706 return -1; 707 708 for (; y < ly; ++y, x = 0) { 709 if (x < w->_first[y]) 710 w->_first[y] = (short) x; 711 712 for (cp = w->_line[y], i = 0; x < w->_maxx; ++x, ++i) { 713 cp[x] = w->_bg; 714 715 /* The start of each new character will be set true 716 * while internal columns of the character will be 717 * reset to false. 718 */ 719 cp[x]._f = (short) (i % width == 0); 720 } 721 722 if (w->_last[y] < x) 723 w->_last[y] = (short) x; 724 } 725 726 if (x < w->_first[y]) 727 w->_first[y] = (short) x; 728 729 for (cp = w->_line[y], i = 0; x <= lx; ++x, ++i) { 730 cp[x] = w->_bg; 731 732 /* The start of each new character will be set true 733 * while internal columns of the character will be 734 * reset to false. 735 */ 736 cp[x]._f = (short) (i % width == 0); 737 } 738 739 if (w->_last[y] < x) 740 w->_last[y] = (short) x; 741 742 return 0; 743 } 744 745 /* 746 * Expand the character to the left or right of the given position. 747 * Return the value returned by __m_cc_replace(). 748 */ 749 int 750 __m_cc_expand(w, y, x, side) 751 WINDOW *w; 752 int y, x, side; 753 { 754 cchar_t cc; 755 int dx, width; 756 757 width = __m_cc_width(&w->_line[y][x]); 758 759 if (side < 0) 760 dx = __m_cc_next(w, y, x) - width; 761 else if (0 < side) 762 dx = __m_cc_first(w, y, x); 763 else 764 return -1; 765 766 /* __m_cc_replace() will erase the region containing 767 * the character we want to expand. 768 */ 769 cc = w->_line[y][x]; 770 771 return __m_cc_replace(w, y, dx, &cc, 0); 772 } 773 774 /* 775 * Return true if characters are equal. 776 * 777 * NOTE to guarantee correct results, make sure that both 778 * characters have been passed through __m_cc_sort(). 779 */ 780 int 781 __m_cc_compare(c1, c2, exact) 782 const cchar_t *c1, *c2; 783 int exact; 784 { 785 int i; 786 787 if (exact && c1->_f != c2->_f) 788 return 0; 789 if (c1->_n != c2->_n) 790 return 0; 791 if ((c1->_at & ~WA_COOKIE) != (c2->_at & ~WA_COOKIE)) 792 return 0; 793 if (c1->_co != c2->_co) 794 return 0; 795 796 for (i = 0; i < c1->_n; ++i) 797 if (c1->_wc[i] != c2->_wc[i]) 798 return 0; 799 800 return 1; 801 } 802 803 /* 804 * Write to the stream the character portion of a cchar_t. 805 */ 806 int 807 __m_cc_write(cc) 808 const cchar_t *cc; 809 { 810 size_t i, j; 811 char mb[MB_LEN_MAX]; 812 813 errno = 0; 814 for (i = 0; i < cc->_n; ++i) { 815 j = wcrtomb(mb, cc->_wc[i], &__m_screen->_state); 816 if (errno != 0) 817 return EOF; 818 if (fwrite(mb, sizeof *mb, j, __m_screen->_of) == 0) 819 return EOF; 820 } 821 822 return 0; 823 } 824