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