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