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