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
write_string(int byte,t_string * sp)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
wistombs(char * mbs,const wint_t * wis,int n)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
wistowcs(wchar_t * wcs,const wint_t * wis,int n)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
__m_touch_locs(WINDOW * w,int row,int firstCol,int lastCol)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
__m_chtype_cc(chtype ch,cchar_t * cc)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
__m_cc_chtype(const cchar_t * cc)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
__m_cc_mbs(const cchar_t * cc,char * mbs,int n)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
__m_tty_wc(int index,wchar_t * wcp)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
__m_mbs_cc(const char * mbs,attr_t at,short co,cchar_t * cc)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
__m_wcs_cc(const wchar_t * wcs,attr_t at,short co,cchar_t * cc)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
__m_wc_cc(wint_t wc,cchar_t * cc)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
__m_cc_sort(cchar_t * cc)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
__m_cc_first(WINDOW * w,int y,int x)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
__m_cc_next(WINDOW * w,int y,int x)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
__m_cc_islast(WINDOW * w,int y,int x)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
__m_cc_replace(WINDOW * w,int y,int x,const cchar_t * cc,int as_is)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
__m_do_scroll(WINDOW * w,int y,int x,int * yp,int * xp)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
__m_cc_add(WINDOW * w,int y,int x,const cchar_t * cc,int as_is,int * yp,int * xp)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
__m_cc_add_k(WINDOW * w,int y,int x,const cchar_t * cc,int as_is,int * yp,int * xp)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
__m_cc_modify(WINDOW * w,int y,int x,const cchar_t * cc)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
__m_cc_erase_in_line(WINDOW * w,int y,int x,int lx,int bgWidth)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
__m_cc_erase_in_line_sub(WINDOW * w,int y,int x,int lx,int bgWidth,int parentBGWidth)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
__m_cc_erase(WINDOW * w,int y,int x,int ly,int lx)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
__m_cc_expand(WINDOW * w,int y,int x,int side)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
__m_cc_equal(const cchar_t * c1,const cchar_t * c2)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
__m_cc_compare(const cchar_t * c1,const cchar_t * c2,int exact)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
__m_cc_write(const cchar_t * cc)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