xref: /titanic_50/usr/src/common/smbsrv/smb_oem.c (revision 45179f4335d029f1129eb7283f8087740f0395f1)
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 /*
29  * Support for oem <-> unicode translations.
30  */
31 
32 #ifndef _KERNEL
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <thread.h>
36 #include <synch.h>
37 #include <string.h>
38 #endif /* _KERNEL */
39 #include <smbsrv/alloc.h>
40 #include <smbsrv/string.h>
41 #include <smbsrv/oem.h>
42 #include <sys/byteorder.h>
43 /*
44  * name: Name used to show on the telnet/GUI.
45  * filename: The actual filename contains the codepage.
46  * doublebytes: The codepage is double or single byte.
47  * oempage: The oempage is used to convert Unicode to OEM chars.
48  *		Memory needs to be allocated for value field of oempage
49  *		to store the entire table.
50  * unipage: The unipage is used to convert OEM to Unicode chars.
51  *		Memory needs to be allocated for value field of unipage
52  *		to store the entire table.
53  * valid: This field indicates if the page is valid or not.
54  * ref: This ref count is used to keep track of the usage of BOTH
55  *		oempage and unipage.
56  * Note: If the cpid of the table is changed, please change the
57  * codepage_id in oem.h as well.
58  */
59 typedef struct oem_codepage {
60 	char *filename;
61 	unsigned int bytesperchar;
62 	oempage_t oempage;
63 	oempage_t unicodepage;
64 	unsigned int valid;
65 	unsigned int ref;
66 } oem_codepage_t;
67 
68 static oem_codepage_t oemcp_table[] = {
69 	{"850.cpg", 1, {0, 0}, {0, 0}, 0, 0},	/* Multilingual Latin1 */
70 	{"950.cpg", 2, {1, 0}, {1, 0}, 0, 0},	/* Chinese Traditional */
71 	{"1252.cpg", 1, {2, 0}, {2, 0}, 0, 0},	/* MS Latin1 */
72 	{"949.cpg", 2, {3, 0}, {3, 0}, 0, 0},	/* Korean */
73 	{"936.cpg", 2, {4, 0}, {4, 0}, 0, 0},	/* Chinese Simplified */
74 	{"932.cpg", 2, {5, 0}, {5, 0}, 0, 0},	/* Japanese */
75 	{"852.cpg", 1, {6, 0}, {6, 0}, 0, 0},	/* Multilingual Latin2 */
76 	{"1250.cpg", 1, {7, 0}, {7, 0}, 0, 0},	/* MS Latin2 */
77 	{"1253.cpg", 1, {8, 0}, {8, 0}, 0, 0},	/* MS Greek */
78 	{"737.cpg", 1, {9, 0}, {9, 0}, 0, 0},	/* Greek */
79 	{"1254.cpg", 1, {10, 0}, {10, 0}, 0, 0}, /* MS Turkish */
80 	{"857.cpg", 1, {11, 0}, {11, 0}, 0, 0},	/* Multilingual Latin5 */
81 	{"1251.cpg", 1, {12, 0}, {12, 0}, 0, 0}, /* MS Cyrillic */
82 	{"866.cpg", 1, {13, 0}, {13, 0}, 0, 0},	/* Cyrillic II */
83 	{"1255.cpg", 1, {14, 0}, {14, 0}, 0, 0}, /* MS Hebrew */
84 	{"862.cpg", 1, {15, 0}, {15, 0}, 0, 0},	/* Hebrew */
85 	{"1256.cpg", 1, {16, 0}, {16, 0}, 0, 0}, /* MS Arabic */
86 	{"720.cpg", 1, {17, 0}, {17, 0}, 0, 0}	/* Arabic */
87 };
88 
89 static language lang_table[] = {
90 	{"Arabic", OEM_CP_IND_720, OEM_CP_IND_1256},
91 	{"Brazilian", OEM_CP_IND_850, OEM_CP_IND_1252},
92 	{"Chinese Traditional", OEM_CP_IND_950, OEM_CP_IND_950},
93 	{"Chinese Simplified", OEM_CP_IND_936, OEM_CP_IND_936},
94 	{"Czech", OEM_CP_IND_852, OEM_CP_IND_1250},
95 	{"Danish", OEM_CP_IND_850, OEM_CP_IND_1252},
96 	{"Dutch", OEM_CP_IND_850, OEM_CP_IND_1252},
97 	{"English", OEM_CP_IND_850, OEM_CP_IND_1252},
98 	{"Finnish", OEM_CP_IND_850, OEM_CP_IND_1252},
99 	{"French", OEM_CP_IND_850, OEM_CP_IND_1252},
100 	{"German", OEM_CP_IND_850, OEM_CP_IND_1252},
101 	{"Greek", OEM_CP_IND_737, OEM_CP_IND_1253},
102 	{"Hebrew", OEM_CP_IND_862, OEM_CP_IND_1255},
103 	{"Hungarian", OEM_CP_IND_852, OEM_CP_IND_1250},
104 	{"Italian", OEM_CP_IND_850, OEM_CP_IND_1252},
105 	{"Japanese", OEM_CP_IND_932, OEM_CP_IND_932},
106 	{"Korean", OEM_CP_IND_949, OEM_CP_IND_949},
107 	{"Norwegian", OEM_CP_IND_850, OEM_CP_IND_1252},
108 	{"Polish", OEM_CP_IND_852, OEM_CP_IND_1250},
109 	{"Russian", OEM_CP_IND_866, OEM_CP_IND_1251},
110 	{"Slovak", OEM_CP_IND_852, OEM_CP_IND_1250},
111 	{"Slovenian", OEM_CP_IND_852, OEM_CP_IND_1250},
112 	{"Spanish", OEM_CP_IND_850, OEM_CP_IND_1252},
113 	{"Swedish", OEM_CP_IND_850, OEM_CP_IND_1252},
114 	{"Turkish", OEM_CP_IND_857, OEM_CP_IND_1254}
115 };
116 
117 
118 
119 /*
120  * The oem_default_smb_cp is the default smb codepage for English.
121  * It is actually codepage 850.
122  */
123 mts_wchar_t oem_default_smb_cp[256] = {
124 	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
125 	0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
126 	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
127 	0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
128 	0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
129 	0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
130 	0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
131 	0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
132 	0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
133 	0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
134 	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
135 	0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
136 	0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
137 	0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
138 	0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
139 	0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
140 	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
141 	0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
142 	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
143 	0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
144 	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
145 	0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
146 	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
147 	0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
148 	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
149 	0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
150 	0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
151 	0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
152 	0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
153 	0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
154 	0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
155 	0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
156 };
157 
158 
159 
160 /*
161  * The oem_default_telnet_cp is the default telnet codepage for English.
162  * It is actually codepage 1252.
163  */
164 mts_wchar_t oem_default_telnet_cp[256] = {
165 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
166 	0x9, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x10,
167 	0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
168 	0x19, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x20,
169 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
170 	0x29, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x30,
171 	0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
172 	0x39, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x40,
173 	0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
174 	0x49, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x50,
175 	0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
176 	0x59, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x60,
177 	0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
178 	0x69, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x70,
179 	0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
180 	0x79, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x20AC,
181 	0x81, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
182 	0x2030, 0x160, 0x2039, 0x152, 0x8D, 0x017D, 0x8F, 0x90,
183 	0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC,
184 	0x2122, 0x161, 0x203A, 0x153, 0x9D, 0x017E, 0x178, 0x00A0,
185 	0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8,
186 	0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0,
187 	0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8,
188 	0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0,
189 	0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8,
190 	0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0,
191 	0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8,
192 	0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0,
193 	0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8,
194 	0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0,
195 	0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8,
196 	0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
197 };
198 
199 
200 #define	MAX_OEMPAGES (sizeof (oemcp_table) / sizeof (oemcp_table[0]))
201 #define	MAX_UNI_IDX 65536
202 
203 
204 
205 /*
206  * oem_codepage_bytesperchar
207  *
208  * This function returns the max bytes per oem char for the specified
209  * oem table. This basically shows if the oem codepage is single or
210  * double bytes.
211  */
212 static unsigned int
213 oem_codepage_bytesperchar(unsigned int cpid)
214 {
215 	if (cpid >= MAX_OEMPAGES)
216 		return (0);
217 	else
218 		return (oemcp_table[cpid].bytesperchar);
219 }
220 
221 
222 
223 /*
224  * oem_get_codepage_path
225  *
226  * This function will get the codepage path.
227  */
228 const char *
229 oem_get_codepage_path(void)
230 {
231 #ifdef PBSHORTCUT /* */
232 	const char *path = getenv("codepage.oem.directory");
233 	if (path == 0)
234 		return ("/");
235 	else
236 		return (path);
237 #else /* PBSHORTCUT */
238 	return ("/");
239 #endif /* PBSHORTCUT */
240 }
241 
242 /*
243  * oem_codepage_init
244  *
245  * This function will init oem page via the cpid of the oem table.
246  * The function oem_codepage_free must be called when the oempage is
247  * no longer needed to free up the allocated memory. If the codepage is
248  * successfully initialized, zero will be the return value; otherwise
249  * -1 will be the return value.
250  */
251 int
252 oem_codepage_init(unsigned int cpid)
253 {
254 #ifndef _KERNEL
255 	FILE *fp;
256 	static mutex_t mutex;
257 	char buf[32];
258 	char filePath[100];
259 #endif /* _KERNEL */
260 	unsigned int max_oem_index;
261 	const char *codepagePath = oem_get_codepage_path();
262 	mts_wchar_t *default_oem_cp = 0;
263 	oem_codepage_t *oemcp;
264 
265 	/*
266 	 * The OEM codepages 850 and 1252 are stored in kernel; therefore,
267 	 * no need for codepagePath to be defined to work.
268 	 */
269 	if (cpid >= MAX_OEMPAGES ||
270 	    (codepagePath == 0 &&
271 	    cpid != oem_default_smb_cpid && cpid != oem_default_telnet_cpid))
272 		return (-1);
273 
274 	max_oem_index = 1 << oem_codepage_bytesperchar(cpid) * 8;
275 	/*
276 	 * Use mutex so no two same index can be initialize
277 	 * at the same time.
278 	 */
279 #ifndef _KERNEL
280 	(void) mutex_lock(&mutex);
281 #endif /* _KERNEL */
282 
283 	oemcp = &oemcp_table[cpid];
284 	if (oemcp->valid) {
285 		oemcp->valid++;
286 #ifndef _KERNEL
287 		(void) mutex_unlock(&mutex);
288 #endif /* _KERNEL */
289 		return (0);
290 	}
291 
292 	oemcp->oempage.value =
293 	    MEM_ZALLOC("oem", max_oem_index * sizeof (mts_wchar_t));
294 	if (oemcp->oempage.value == 0) {
295 #ifndef _KERNEL
296 		(void) mutex_unlock(&mutex);
297 #endif /* _KERNEL */
298 		return (-1);
299 	}
300 
301 	oemcp->unicodepage.value =
302 	    MEM_ZALLOC("oem", MAX_UNI_IDX * sizeof (mts_wchar_t));
303 	if (oemcp->unicodepage.value == 0) {
304 		MEM_FREE("oem", oemcp->oempage.value);
305 		oemcp->oempage.value = 0;
306 #ifndef _KERNEL
307 		(void) mutex_unlock(&mutex);
308 #endif /* _KERNEL */
309 		return (-1);
310 	}
311 
312 	/*
313 	 * The default English page is stored in kernel.
314 	 * Therefore, no need to go to codepage files.
315 	 */
316 #ifndef _KERNEL
317 	if (cpid == oem_default_smb_cpid)
318 		default_oem_cp = oem_default_smb_cp;
319 	else if (cpid == oem_default_telnet_cpid)
320 		default_oem_cp = oem_default_telnet_cp;
321 	else
322 		default_oem_cp = 0;
323 #else /* _KERNEL */
324 	default_oem_cp = oem_default_smb_cp;
325 #endif /* _KERNEL */
326 
327 	if (default_oem_cp) {
328 		int i;
329 
330 		for (i = 0; i < max_oem_index; i++) {
331 			oemcp->oempage.value[i] = default_oem_cp[i];
332 			oemcp->unicodepage.value[default_oem_cp[i]] =
333 			    (mts_wchar_t)i;
334 		}
335 #ifdef _KERNEL
336 	}
337 	/*
338 	 * XXX This doesn't seem right.  How do we handle the situation
339 	 * where default_oem_cp == 0 in the kernel?
340 	 * Is this a PBSHORTCUT?
341 	 */
342 #else
343 	} else {
344 
345 		/*
346 		 * The codepage is not one of the default that stores
347 		 * in the include
348 		 * file; therefore, we need to read from the file.
349 		 */
350 		(void) snprintf(filePath, sizeof (filePath),
351 		    "%s/%s", codepagePath, oemcp->filename);
352 		fp = fopen(filePath, "r");
353 
354 		if (fp == 0) {
355 			MEM_FREE("oem", oemcp->oempage.value);
356 			MEM_FREE("oem", oemcp->unicodepage.value);
357 #ifndef _KERNEL
358 			(void) mutex_unlock(&mutex);
359 #endif /* _KERNEL */
360 			return (-1);
361 		}
362 
363 		while (fgets(buf, 32, fp) != 0) {
364 			char *endptr;
365 			unsigned int oemval, unival;
366 
367 			endptr = (char *)strchr(buf, ' ');
368 			if (endptr == 0) {
369 				continue;
370 			}
371 
372 			oemval = strtol(buf, &endptr, 0);
373 			unival = strtol(endptr+1, 0, 0);
374 
375 			if (oemval >= max_oem_index || unival >= MAX_UNI_IDX) {
376 				continue;
377 			}
378 
379 			oemcp->oempage.value[oemval] = unival;
380 			oemcp->unicodepage.value[unival] = oemval;
381 		}
382 		(void) fclose(fp);
383 	}
384 #endif /* _KERNEL */
385 
386 	oemcp->valid = 1;
387 #ifndef _KERNEL
388 	(void) mutex_unlock(&mutex);
389 #endif /* _KERNEL */
390 	return (0);
391 }
392 
393 
394 
395 
396 /*
397  * oem_codepage_free
398  *
399  * This function will clear the valid bit and free the memory
400  * allocated to the oem/unipage by oem_codepage_init if the ref count
401  * is zero.
402  */
403 void
404 oem_codepage_free(unsigned int cpid)
405 {
406 	oem_codepage_t *oemcp;
407 
408 	if (cpid >= MAX_OEMPAGES || !oemcp_table[cpid].valid)
409 		return;
410 
411 	oemcp = &oemcp_table[cpid];
412 	oemcp->valid--;
413 
414 	if (oemcp->ref != 0 || oemcp->valid != 0)
415 		return;
416 
417 	if (oemcp->oempage.value != 0) {
418 		MEM_FREE("oem", oemcp->oempage.value);
419 		oemcp->oempage.value = 0;
420 	}
421 
422 	if (oemcp->unicodepage.value != 0) {
423 		MEM_FREE("oem", oemcp->unicodepage.value);
424 		oemcp->unicodepage.value = 0;
425 	}
426 }
427 
428 
429 
430 /*
431  * oem_get_oempage
432  *
433  * This function will return the current oempage and increment
434  * the ref count. The function oem_release_page should always
435  * be called when finish using the oempage to decrement the
436  * ref count.
437  */
438 static oempage_t *
439 oem_get_oempage(unsigned int cpid)
440 {
441 	if (cpid >= MAX_OEMPAGES)
442 		return (0);
443 
444 	if (oemcp_table[cpid].valid) {
445 		oemcp_table[cpid].ref++;
446 		return (&oemcp_table[cpid].oempage);
447 	}
448 	return (0);
449 }
450 
451 
452 
453 /*
454  * oem_get_unipage
455  *
456  * This function will return the current unipage and increment
457  * the ref count. The function oem_release_page should always
458  * be called when finish using the unipage to decrement the
459  * ref count.
460  */
461 static oempage_t *
462 oem_get_unipage(unsigned int cpid)
463 {
464 	if (cpid >= MAX_OEMPAGES)
465 		return (0);
466 
467 	if (oemcp_table[cpid].valid) {
468 		oemcp_table[cpid].ref++;
469 		return (&oemcp_table[cpid].unicodepage);
470 	}
471 	return (0);
472 }
473 
474 
475 
476 /*
477  * oem_release_page
478  *
479  * This function will decrement the ref count and check the valid
480  * bit. It will free the memory allocated for the pages
481  * if the
482  * valid bit is not set, ref count is zero and the page is not
483  * already freed.
484  */
485 static void
486 oem_release_page(oempage_t *page)
487 {
488 	oem_codepage_t *oemcp = &oemcp_table[page->cpid];
489 
490 	page = 0;
491 
492 	if (oemcp->ref > 0)
493 		oemcp->ref--;
494 
495 	if (oemcp->ref != 0 || oemcp->valid)
496 		return;
497 
498 	if (oemcp->oempage.value != 0) {
499 		MEM_FREE("oem", oemcp->oempage.value);
500 		oemcp->oempage.value = 0;
501 	}
502 
503 	if (oemcp->unicodepage.value != 0) {
504 		MEM_FREE("oem", oemcp->unicodepage.value);
505 		oemcp->unicodepage.value = 0;
506 	}
507 }
508 
509 
510 
511 /*
512  * unicodestooems
513  *
514  * Convert unicode string to oem string. The function will stop
515  * converting the unicode string when size nbytes - 1 is reached
516  * or when there is not enough room to store another unicode.
517  * If the function is called when the codepage is not initialized
518  * or when the codepage initialize failed, it will return 0.
519  * Otherwise, the total # of the converted unicode is returned.
520  */
521 size_t
522 unicodestooems(
523     char *oemstring,
524     const mts_wchar_t *unicodestring,
525     size_t nbytes,
526     unsigned int cpid)
527 {
528 	oempage_t *unipage;
529 	unsigned int count = 0;
530 	mts_wchar_t oemchar;
531 
532 	if (cpid >= MAX_OEMPAGES)
533 		return (0);
534 
535 	if (unicodestring == 0 || oemstring == 0)
536 		return (0);
537 
538 	if ((unipage = oem_get_unipage(cpid)) == 0)
539 		return (0);
540 
541 	while ((oemchar = unipage->value[*unicodestring]) != 0) {
542 		if (oemchar & 0xff00 && nbytes >= MTS_MB_CHAR_MAX) {
543 			*oemstring++ = oemchar >> 8;
544 			*oemstring++ = (char)oemchar;
545 			nbytes -= 2;
546 		} else if (nbytes > 1) {
547 			*oemstring++ = (char)oemchar;
548 			nbytes--;
549 		} else
550 			break;
551 
552 		count++;
553 		unicodestring++;
554 	}
555 
556 	*oemstring = 0;
557 
558 	oem_release_page(unipage);
559 
560 	return (count);
561 }
562 
563 
564 
565 /*
566  * oemstounicodes
567  *
568  * Convert oem string to unicode string. The function will stop
569  * converting the oem string when unicodestring len reaches nwchars - 1.
570  * or when there is not enough room to store another oem char.
571  * If the function is called when the codepage is not initialized
572  * or when the codepage initialize failed, it will return 0.
573  * Otherwise, the total # of the converted oem chars is returned.
574  * The oem char can be either 1 or 2 bytes.
575  */
576 size_t
577 oemstounicodes(
578     mts_wchar_t *unicodestring,
579     const char *oemstring,
580     size_t nwchars,
581     unsigned int cpid)
582 {
583 	oempage_t *oempage;
584 	size_t count = nwchars;
585 	mts_wchar_t oemchar;
586 
587 	if (cpid >= MAX_OEMPAGES)
588 		return (0);
589 
590 	if (unicodestring == 0 || oemstring == 0)
591 		return (0);
592 
593 	if ((oempage = oem_get_oempage(cpid)) == 0)
594 		return (0);
595 
596 	while ((oemchar = (mts_wchar_t)*oemstring++ & 0xff) != 0) {
597 		/*
598 		 * Cannot find one byte oemchar in table. Must be
599 		 * a lead byte. Try two bytes.
600 		 */
601 
602 		if ((oempage->value[oemchar] == 0) && (oemchar != 0)) {
603 			oemchar = oemchar << 8 | (*oemstring++ & 0xff);
604 			if (oempage->value[oemchar] == 0) {
605 				*unicodestring = 0;
606 				break;
607 			}
608 		}
609 #ifdef _BIG_ENDIAN
610 		*unicodestring = LE_IN16(&oempage->value[oemchar]);
611 #else
612 		*unicodestring = oempage->value[oemchar];
613 #endif
614 		count--;
615 		unicodestring++;
616 	}
617 
618 	*unicodestring = 0;
619 
620 	oem_release_page(oempage);
621 
622 	return (nwchars - count);
623 }
624 
625 /*
626  * oem_get_lang_table
627  *
628  * This function returns a pointer to the language table.
629  */
630 language *
631 oem_get_lang_table(void)
632 {
633 	return (lang_table);
634 }
635 
636 /*
637  * oem_no_of_languages
638  *
639  * This function returns total languages support in the system.
640  */
641 int
642 oem_no_of_languages(void)
643 {
644 	return (sizeof (lang_table)/sizeof (lang_table[0]));
645 }
646 
647 
648 #ifndef _KERNEL
649 #if 1
650 /*
651  * TESTING Functions
652  */
653 void
654 oemcp_print(unsigned int cpid)
655 {
656 	unsigned int bytesperchar, max_index, i;
657 	oempage_t *oempage, *unipage;
658 	unsigned int counter = 0;
659 
660 	if (cpid >= MAX_OEMPAGES) {
661 		(void) printf("oemcp cpid %d is invalid\n", cpid);
662 		return;
663 	}
664 
665 	if ((oempage = oem_get_oempage(cpid)) == 0) {
666 		(void) printf("oemcp of cpid %d is invalid\n", cpid);
667 		return;
668 	}
669 
670 	if ((unipage = oem_get_unipage(cpid)) == 0) {
671 		(void) printf("unicp of cpid %d is invalid\n", cpid);
672 		return;
673 	}
674 
675 	if ((bytesperchar = oem_codepage_bytesperchar(cpid)) == 0) {
676 		(void) printf("bytesperchar of cpid %d is not correct\n", cpid);
677 		return;
678 	}
679 
680 	max_index = 1 << bytesperchar * 8;
681 
682 	(void) printf("OEMPAGE:\n");
683 	for (i = 0; i < max_index; i++) {
684 		if ((counter + 1) % 4 == 0 &&
685 		    (oempage->value[i] != 0 || i == 0)) {
686 			(void) printf("%x %x\n", i, oempage->value[i]);
687 			counter++;
688 		} else if (oempage->value[i] != 0 || i == 0) {
689 			(void) printf("%x %x, ", i, oempage->value[i]);
690 			counter++;
691 		}
692 	}
693 	counter = 0;
694 	(void) printf("\n\nUNIPAGE:\n");
695 	for (i = 0; i < 65536; i++) {
696 		if ((counter + 1) % 8 == 0 &&
697 		    (unipage->value[i] != 0 || i == 0)) {
698 			(void) printf("%x %x\n", i, unipage->value[i]);
699 			counter++;
700 		} else if (unipage->value[i] != 0 || i == 0) {
701 			(void) printf("%x %x, ", i, unipage->value[i]);
702 			counter++;
703 		}
704 	}
705 	(void) printf("\n");
706 	oem_release_page(oempage);
707 	oem_release_page(unipage);
708 }
709 
710 
711 
712 void
713 oemstringtest(unsigned int cpid)
714 {
715 	unsigned char *c, *cbuf;
716 	unsigned char cbuf1[100] = {0xfe, 0xfd, 0xf2, 0xe9,
717 		0x63, 0xce, 0xdb, 0x8c, 0x9c, 0x21, 0};
718 	unsigned char cbuf2[100] = {0xfe, 0xfc, 0x63, 0x81, 0x42,
719 		0x91, 0x40, 0x24, 0xff, 0x49};
720 	mts_wchar_t buf[100], *wc;
721 
722 	if (cpid == 1)
723 		cbuf = cbuf1;
724 	else if (cpid == 2)
725 		cbuf = cbuf2;
726 
727 	/*
728 	 * Before oem->uni conversion.
729 	 */
730 	(void) printf("Before oem->uni conversion: ");
731 	for (c = cbuf; *c != 0; c++)
732 		(void) printf("%x ", *c);
733 	(void) printf("\n");
734 
735 	/*
736 	 * oem->uni conversion
737 	 */
738 	(void) oemstounicodes(buf, (const char *)cbuf, 100, cpid);
739 
740 	/*
741 	 * After oem->uni conversion.
742 	 */
743 	(void) printf("After oem->uni conversion: ");
744 	for (wc = buf; *wc != 0; wc++)
745 		(void) printf("%x ", *wc);
746 	(void) printf("\n");
747 
748 	/*
749 	 * uni->oem conversion
750 	 */
751 	(void) unicodestooems((char *)cbuf, buf, 100, cpid);
752 
753 	/*
754 	 * After uni->oem conversion.
755 	 */
756 	(void) printf("After uni->oem conversion: ");
757 	for (c = cbuf; *c != 0; c++)
758 		(void) printf("%x ", *c);
759 	(void) printf("\n");
760 }
761 #endif
762 #endif /* _KERNEL */
763