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/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include "lint.h"
28#include "mtlib.h"
29#include <sys/types.h>
30#include <ctype.h>
31#include <string.h>
32#include <stdlib.h>
33#include <euc.h>
34#include <widec.h>
35#include <wctype.h>
36#include <limits.h>
37#include <synch.h>
38#include <thread.h>
39#include <libintl.h>
40#include "libc.h"
41
42#include <locale.h>
43#include <dlfcn.h>
44#include "_loc_path.h"
45
46static int	wdchkind_C(wchar_t);
47static int	(*wdchknd)(wchar_t) = wdchkind_C;
48static int	wdbindf_C(wchar_t, wchar_t, int);
49static int	(*wdbdg)(wchar_t, wchar_t, int) = wdbindf_C;
50static wchar_t	*wddelim_C(wchar_t, wchar_t, int);
51static wchar_t	*(*wddlm)(wchar_t, wchar_t, int) = wddelim_C;
52static wchar_t	(*mcfllr)(void) = NULL;
53static int	(*mcwrp)(void) = NULL;
54static void	*modhandle = NULL;
55static int	initialized = 0;
56
57static int
58_wdinitialize(void)
59{
60#define	_DFLTLOCPATH_LEN	(sizeof (_DFLT_LOC_PATH) - 1)
61#define	_WDMODPATH_LEN		(sizeof (_WDMOD_PATH) - 1)
62	char	wdmodpath[PATH_MAX];
63	char	*loc;
64	size_t	loclen;
65	locale_t	curloc;
66
67	initialized = 1;
68
69	if (modhandle)
70		(void) dlclose(modhandle);
71
72	curloc = uselocale(NULL);
73	loc = current_locale(curloc, LC_CTYPE);
74	loclen = strlen(loc);
75	if (_DFLTLOCPATH_LEN + loclen + _WDMODPATH_LEN >= sizeof (wdmodpath)) {
76		/* pathname too long */
77		modhandle = NULL;
78		goto C_fallback;
79	}
80
81	(void) strcpy(wdmodpath, _DFLT_LOC_PATH);
82	(void) strcpy(wdmodpath + _DFLTLOCPATH_LEN, loc);
83	(void) strcpy(wdmodpath + _DFLTLOCPATH_LEN + loclen, _WDMOD_PATH);
84
85	if ((modhandle = dlopen(wdmodpath, RTLD_LAZY)) != NULL) {
86		wdchknd = (int(*)(wchar_t))dlsym(modhandle, "_wdchkind_");
87		if (wdchknd == NULL)
88			wdchknd = wdchkind_C;
89		wdbdg = (int(*)(wchar_t, wchar_t, int))dlsym(modhandle,
90		    "_wdbindf_");
91		if (wdbdg == NULL)
92			wdbdg = wdbindf_C;
93		wddlm = (wchar_t *(*)(wchar_t, wchar_t, int))
94		    dlsym(modhandle, "_wddelim_");
95		if (wddlm == NULL)
96			wddlm = wddelim_C;
97		mcfllr = (wchar_t(*)(void))dlsym(modhandle, "_mcfiller_");
98		mcwrp = (int(*)(void))dlsym(modhandle, "_mcwrap_");
99		return ((mcfllr && mcwrp) ? 0 : -1);
100	}
101
102C_fallback:
103	wdchknd = wdchkind_C;
104	wdbdg = wdbindf_C;
105	wddlm = wddelim_C;
106	mcfllr = NULL;
107	mcwrp = NULL;
108	return (-1);
109}
110
111/*
112 * wdinit() initializes other word-analyzing routines according to the
113 * current locale.  Programmers are supposed to call this routine every
114 * time the locale for the LC_CTYPE category is changed.  It returns 0
115 * when every initialization completes successfully, or -1 otherwise.
116 */
117/* XXX: wdinit() is not exported from libc.  Should it be? */
118int
119wdinit()
120{
121	int res;
122
123	callout_lock_enter();
124	res = _wdinitialize();
125	callout_lock_exit();
126	return (res);
127}
128
129/*
130 * wdchkind() returns a non-negative integral value unique to the kind
131 * of the character represented by given argument.
132 */
133int
134wdchkind(wchar_t wc)
135{
136	int i;
137
138	callout_lock_enter();
139	if (!initialized)
140		(void) _wdinitialize();
141	i = (*wdchknd)(wc);
142	callout_lock_exit();
143	return (i);
144}
145static int
146wdchkind_C(wchar_t wc)
147{
148	switch (wcsetno(wc)) {
149	case 1:
150		return (2);
151	case 2:
152		return (3);
153	case 3:
154		return (4);
155	case 0:
156		return (isascii(wc) &&
157		    (isalpha(wc) || isdigit(wc) || wc == ' '));
158	}
159	return (0);
160}
161
162/*
163 * wdbindf() returns an integral value (0 - 7) indicating binding
164 *  strength of two characters represented by the first two arguments.
165 * It returns -1 when either of the two character is not printable.
166 */
167/*ARGSUSED*/
168int
169wdbindf(wchar_t wc1, wchar_t wc2, int type)
170{
171	int i;
172
173	callout_lock_enter();
174	if (!initialized)
175		(void) _wdinitialize();
176	if (!iswprint(wc1) || !iswprint(wc2)) {
177		callout_lock_exit();
178		return (-1);
179	}
180	i = (*wdbdg)(wc1, wc2, type);
181	callout_lock_exit();
182	return (i);
183}
184/*ARGSUSED*/
185static int
186wdbindf_C(wchar_t wc1, wchar_t wc2, int type)
187{
188	if (csetlen(wc1) > 1 && csetlen(wc2) > 1)
189		return (4);
190	return (6);
191}
192
193/*
194 * wddelim() returns a pointer to a null-terminated word delimiter
195 * string in wchar_t type that is thought most appropriate to join
196 * a text line ending with the first argument and a line beginning
197 * with the second argument, with.  When either of the two character
198 * is not printable it returns a pointer to a null wide character.
199 */
200/*ARGSUSED*/
201wchar_t *
202wddelim(wchar_t wc1, wchar_t wc2, int type)
203{
204	wchar_t *i;
205
206	callout_lock_enter();
207	if (!initialized)
208		(void) _wdinitialize();
209	if (!iswprint(wc1) || !iswprint(wc2)) {
210		callout_lock_exit();
211		return ((wchar_t *)L"");
212	}
213	i = (*wddlm)(wc1, wc2, type);
214	callout_lock_exit();
215	return (i);
216}
217/*ARGSUSED*/
218static wchar_t *
219wddelim_C(wchar_t wc1, wchar_t wc2, int type)
220{
221	return ((wchar_t *)L" ");
222}
223
224/*
225 * mcfiller returns a printable ASCII character suggested for use in
226 * filling space resulted by a multicolumn character at the right margin.
227 */
228wchar_t
229mcfiller(void)
230{
231	wchar_t fillerchar;
232
233	callout_lock_enter();
234	if (!initialized)
235		(void) _wdinitialize();
236	if (mcfllr) {
237		fillerchar = (*mcfllr)();
238		if (!fillerchar)
239			fillerchar = (wchar_t)'~';
240		if (iswprint(fillerchar)) {
241			callout_lock_exit();
242			return (fillerchar);
243		}
244	}
245	callout_lock_exit();
246	return ((wchar_t)'~');
247}
248
249/*
250 * mcwrap returns an integral value indicating if a multicolumn character
251 * on the right margin should be wrapped around on a terminal screen.
252 */
253/* XXX: mcwrap() is not exported from libc.  Should it be? */
254int
255mcwrap(void)
256{
257	callout_lock_enter();
258	if (!initialized)
259		(void) _wdinitialize();
260	if (mcwrp)
261		if ((*mcwrp)() == 0) {
262			callout_lock_exit();
263			return (0);
264		}
265	callout_lock_exit();
266	return (1);
267}
268