Subject: kern/36370: support for cyrilic charsets is needed in msdosfs
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <cheusov@tut.by>
List: netbsd-bugs
Date: 05/21/2007 19:25:01
>Number:         36370
>Category:       kern
>Synopsis:       support for cyrilic charsets is needed in msdosfs
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon May 21 19:25:00 +0000 2007
>Originator:     cheusov@tut.by
>Release:        NetBSD 4.0_BETA2
>Organization:
Best regards, Aleksey Cheusov.
>Environment:
System: NetBSD chen.chizhovka.net 4.0_BETA2 NetBSD 4.0_BETA2 (GENERIC) #16: Mon May 21 00:01:33 EEST 2007 cheusov@chen.chizhovka.net:/srv/src/sys/arch/i386/compile/GENERIC i386
Architecture: i386
Machine: i386
>Description:
MSDOS' fat file system is still alife. Many people use flashes
where FAT file system is ordinary choise.
Unforunately cyrillic character sets are not supported in NetBSD's
msdosfs implementation.

>Fix:
I'm not kernel developer, it's hard for me to hack a kernel
but months ago I created a patch for NetBSD. Now it corresponds
to netbsd-4 branch and works fine for me.

A few notes about it:
- there are some printf() for debugging purposes
- mount_msdos -L and -D options are added
  The default is msdos_msdosfs -L iso-8859-1 -D cp850 is the default
  For cyrillic support -L cp-1251 -D cp866 works for me.
- sys/fs/msdosfs/msdosfs_kiconv.c is kernel space tiny library
  with iconv(3) like API
- Original dos2unix and unix2dos tables from NetBSD code is not the same
  as tr__cp850_to_iso88591 and tr__iso88591_to_cp850. I don't know why.
  I didn't read specs.
- -L and -D options comes from FreeBSD

People around the world use NetBSD and I hope you'll consider
internationalizing msdosfs useful task.

P.S.
FreeBSD has workable code for cyrillic charset for more than 5 years or so.
I took some ideas from it.

Patch:

Index: distrib/sets/lists/comp/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/comp/mi,v
retrieving revision 1.981.2.2
diff -u -r1.981.2.2 mi
--- distrib/sets/lists/comp/mi	8 May 2007 10:45:13 -0000	1.981.2.2
+++ distrib/sets/lists/comp/mi	21 May 2007 18:04:27 -0000
@@ -1273,7 +1273,8 @@
 ./usr/include/msdosfs/denode.h			comp-c-include
 ./usr/include/msdosfs/direntry.h		comp-c-include
 ./usr/include/msdosfs/fat.h			comp-c-include
+./usr/include/msdosfs/msdosfs_kiconv.h		comp-c-include
 ./usr/include/msdosfs/msdosfsmount.h		comp-c-include
 ./usr/include/ndbm.h				comp-c-include
 ./usr/include/net/bpf.h				comp-c-include
Index: sbin/mount_msdos/mount_msdos.c
===================================================================
RCS file: /cvsroot/src/sbin/mount_msdos/mount_msdos.c,v
retrieving revision 1.39
diff -u -r1.39 mount_msdos.c
--- sbin/mount_msdos/mount_msdos.c	16 Oct 2006 03:37:42 -0000	1.39
+++ sbin/mount_msdos/mount_msdos.c	21 May 2007 18:05:15 -0000
@@ -92,7 +92,7 @@
 	mntflags = set_gid = set_uid = set_mask = set_dirmask = set_gmtoff = 0;
 	(void)memset(&args, '\0', sizeof(args));
 
-	while ((c = getopt(argc, argv, "Gsl9u:g:m:M:o:t:")) != -1) {
+	while ((c = getopt(argc, argv, "Gsl9u:g:m:M:o:t:D:L:")) != -1) {
 		switch (c) {
 		case 'G':
 			args.flags |= MSDOSFSMNT_GEMDOSFS;
@@ -132,7 +132,13 @@
 			args.gmtoff = atoi(optarg);
 			set_gmtoff = 1;
 			break;
+		case 'D':
+			strlcpy(args.cs_dos, optarg, sizeof (args.cs_dos));
+			break;
+		case 'L':
+			strlcpy(args.cs_unix, optarg, sizeof (args.cs_unix));
+			break;
 		case '?':
 		default:
 			usage();
Index: sys/fs/msdosfs/Makefile
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/Makefile,v
retrieving revision 1.1
diff -u -r1.1 Makefile
--- sys/fs/msdosfs/Makefile	26 Dec 2002 12:31:32 -0000	1.1
+++ sys/fs/msdosfs/Makefile	21 May 2007 18:05:42 -0000
@@ -2,6 +2,6 @@
 
 INCSDIR= /usr/include/msdosfs
 
-INCS=	bootsect.h bpb.h denode.h direntry.h fat.h msdosfsmount.h
+INCS=	bootsect.h bpb.h denode.h direntry.h fat.h msdosfsmount.h msdosfs_kiconv.h
 
 .include <bsd.kinc.mk>
Index: sys/fs/msdosfs/files.msdosfs
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/files.msdosfs,v
retrieving revision 1.1
diff -u -r1.1 files.msdosfs
--- sys/fs/msdosfs/files.msdosfs	26 Dec 2002 12:31:33 -0000	1.1
+++ sys/fs/msdosfs/files.msdosfs	21 May 2007 18:05:42 -0000
@@ -8,3 +8,4 @@
 file	fs/msdosfs/msdosfs_lookup.c		msdosfs
 file	fs/msdosfs/msdosfs_vfsops.c		msdosfs
 file	fs/msdosfs/msdosfs_vnops.c		msdosfs
+file    fs/msdosfs/msdosfs_kiconv.c             msdosfs
Index: sys/fs/msdosfs/msdosfs_conv.c
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfs_conv.c,v
retrieving revision 1.5
diff -u -r1.5 msdosfs_conv.c
--- sys/fs/msdosfs/msdosfs_conv.c	11 Dec 2005 12:24:25 -0000	1.5
+++ sys/fs/msdosfs/msdosfs_conv.c	21 May 2007 18:05:42 -0000
@@ -60,6 +60,8 @@
 #include <sys/dirent.h>
 #include <sys/vnode.h>
 
+#include <fs/msdosfs/msdosfs_kiconv.h>
+
 /*
  * MSDOSFS include files.
  */
@@ -92,6 +94,14 @@
 u_short lastdtime;
 
 /*
+ * Character class convertors
+ */
+extern void *cs_u2d;
+extern void *cs_d2u;
+extern void *cs_d2uc;
+extern void *cs_u2lc;
+
+/*
  * Convert the unix version of time to dos's idea of time to be used in
  * file timestamps. The passed in unix time is assumed to be in GMT.
  */
@@ -233,114 +243,6 @@
 	tsp->tv_nsec = (dh % 100) * 10000000;
 }
 
-static const u_char
-unix2dos[256] = {
-	0,    0,    0,    0,    0,    0,    0,    0,	/* 00-07 */
-	0,    0,    0,    0,    0,    0,    0,    0,	/* 08-0f */
-	0,    0,    0,    0,    0,    0,    0,    0,	/* 10-17 */
-	0,    0,    0,    0,    0,    0,    0,    0,	/* 18-1f */
-	0,    '!',  0,    '#',  '$',  '%',  '&',  '\'',	/* 20-27 */
-	'(',  ')',  0,    '+',  0,    '-',  0,    0,	/* 28-2f */
-	'0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',	/* 30-37 */
-	'8',  '9',  0,    0,    0,    0,    0,    0,	/* 38-3f */
-	'@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',	/* 40-47 */
-	'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',	/* 48-4f */
-	'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',	/* 50-57 */
-	'X',  'Y',  'Z',  0,    0,    0,    '^',  '_',	/* 58-5f */
-	'`',  'A',  'B',  'C',  'D',  'E',  'F',  'G',	/* 60-67 */
-	'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',	/* 68-6f */
-	'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',	/* 70-77 */
-	'X',  'Y',  'Z',  '{',  0,    '}',  '~',  0,	/* 78-7f */
-	0,    0,    0,    0,    0,    0,    0,    0,	/* 80-87 */
-	0,    0,    0,    0,    0,    0,    0,    0,	/* 88-8f */
-	0,    0,    0,    0,    0,    0,    0,    0,	/* 90-97 */
-	0,    0,    0,    0,    0,    0,    0,    0,	/* 98-9f */
-	0,    0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5,	/* a0-a7 */
-	0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee,	/* a8-af */
-	0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa,	/* b0-b7 */
-	0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8,	/* b8-bf */
-	0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80,	/* c0-c7 */
-	0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8,	/* c8-cf */
-	0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e,	/* d0-d7 */
-	0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1,	/* d8-df */
-	0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80,	/* e0-e7 */
-	0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8,	/* e8-ef */
-	0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6,	/* f0-f7 */
-	0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98,	/* f8-ff */
-};
-
-static const u_char
-dos2unix[256] = {
-	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',	/* 00-07 */
-	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',	/* 08-0f */
-	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',	/* 10-17 */
-	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',	/* 18-1f */
-	 ' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',	/* 20-27 */
-	 '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',	/* 28-2f */
-	 '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',	/* 30-37 */
-	 '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',	/* 38-3f */
-	 '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',	/* 40-47 */
-	 'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',	/* 48-4f */
-	 'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',	/* 50-57 */
-	 'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',	/* 58-5f */
-	 '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',	/* 60-67 */
-	 'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',	/* 68-6f */
-	 'p',  'q',  'r',  's',  't',  'u',  'v',  'w',	/* 70-77 */
-	 'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7f,	/* 78-7f */
-	0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7,	/* 80-87 */
-	0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5,	/* 88-8f */
-	0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9,	/* 90-97 */
-	0xff, 0xd6, 0xdc, 0xf8, 0xa3, 0xd8, 0xd7,  '?',	/* 98-9f */
-	0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba,	/* a0-a7 */
-	0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb,	/* a8-af */
-	 '?',  '?',  '?',  '?',  '?', 0xc1, 0xc2, 0xc0,	/* b0-b7 */
-	0xa9,  '?',  '?',  '?',  '?', 0xa2, 0xa5,  '?',	/* b8-bf */
-	 '?',  '?',  '?',  '?',  '?',  '?', 0xe3, 0xc3,	/* c0-c7 */
-	 '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xa4,	/* c8-cf */
-	0xf0, 0xd0, 0xca, 0xcb, 0xc8,  '?', 0xcd, 0xce,	/* d0-d7 */
-	0xcf,  '?',  '?',  '?',  '?', 0xa6, 0xcc,  '?',	/* d8-df */
-	0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xd5, 0xb5, 0xfe,	/* e0-e7 */
-	0xde, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xaf, 0x3f,	/* e8-ef */
-	0xad, 0xb1,  '?', 0xbe, 0xb6, 0xa7, 0xf7, 0xb8,	/* f0-f7 */
-	0xb0, 0xa8, 0xb7, 0xb9, 0xb3, 0xb2,  '?',  '?',	/* f8-ff */
-};
-
-static const u_char
-u2l[256] = {
-	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */
-	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */
-	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */
-	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */
-	 ' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'', /* 20-27 */
-	 '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/', /* 28-2f */
-	 '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7', /* 30-37 */
-	 '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?', /* 38-3f */
-	 '@',  'a',  'b',  'c',  'd',  'e',  'f',  'g', /* 40-47 */
-	 'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o', /* 48-4f */
-	 'p',  'q',  'r',  's',  't',  'u',  'v',  'w', /* 50-57 */
-	 'x',  'y',  'z',  '[', '\\',  ']',  '^',  '_', /* 58-5f */
-	 '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g', /* 60-67 */
-	 'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o', /* 68-6f */
-	 'p',  'q',  'r',  's',  't',  'u',  'v',  'w', /* 70-77 */
-	 'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7f, /* 78-7f */
-	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */
-	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */
-	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */
-	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */
-	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */
-	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */
-	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */
-	0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */
-	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */
-	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */
-	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */
-	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */
-	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */
-	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */
-	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */
-	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */
-};
-
 /*
  * DOS filenames are made of 2 parts, the name part and the extension part.
  * The name part is 8 characters long and the extension part is 3
@@ -369,11 +271,16 @@
 	 * just have a 0xe5 mean 0xe5 because that is used to mean a freed
 	 * directory slot. Another dos quirk.
 	 */
+	/* Does the comment above consern cp866 or cp850 only? */
 	if (*dn == SLOT_E5)
-		c = dos2unix[0xe5];
+		c = kiconv_char (cs_d2u, '\xe5', '?');
 	else
-		c = dos2unix[*dn];
-	*un++ = lower ? u2l[c] : c;
+		c = kiconv_char (cs_d2u, *dn, '?');
+
+	if (lower)
+		c = kiconv_char (cs_u2lc, c, 0);
+
+	*un++ = c;
 
 	/*
 	 * Copy the rest into the unix filename string, ignoring
@@ -384,8 +291,10 @@
 		;
 
 	for (i = 1; i <= j; i++) {
-		c = dos2unix[dn[i]];
-		*un++ = lower ? u2l[c] : c;
+		c = kiconv_char (cs_d2u, dn [i], '?');
+		if (lower)
+			c = kiconv_char (cs_u2lc, c, 0);
+		*un++ = c;
 		thislong++;
 	}
 	dn += 8;
@@ -398,8 +307,11 @@
 		*un++ = '.';
 		thislong++;
 		for (i = 0; i < 3 && *dn != ' '; i++) {
-			c = dos2unix[*dn++];
-			*un++ = lower ? u2l[c] : c;
+			c = kiconv_char (cs_d2u, *dn, '?');
+			++dn;
+			if (lower)
+				c = kiconv_char (cs_u2lc, c, 0);
+			*un++ = c;
 			thislong++;
 		}
 	}
@@ -431,6 +343,7 @@
 	const u_char *cp, *dp, *dp1;
 	u_char gentext[6], *wcp;
 	int shortlen;
+	int c;
 
 	/*
 	 * Fill the dos filename string with blanks. These are DOS's pad
@@ -494,7 +407,9 @@
 		else
 			l = unlen - (dp - un);
 		for (i = 0, j = 8; i < l && j < 11; i++, j++) {
-			if (dp[i] != (dn[j] = unix2dos[dp[i]])
+			c = kiconv_char (cs_u2d, dp[i], 0);
+			c = dn[j] = kiconv_char (cs_d2uc, c, 0);
+			if (dp[i] != c
 			    && conv != 3)
 				conv = 2;
 			if (!dn[j]) {
@@ -519,7 +434,7 @@
 		if ((*un == ' ') && shortlen)
 			dn[j] = ' ';
 		else
-			dn[j] = unix2dos[*un];
+			dn[j] = kiconv_char (cs_d2uc, kiconv_char (cs_u2d, *un, 0), 0);
 		if ((*un != dn[j])
 		    && conv != 3)
 			conv = 2;
@@ -694,8 +609,11 @@
 				return chksum;
 			return -1;
 		}
-		if (u2l[*cp++] != u2l[*un++] || *cp++)
+		if (kiconv_char (cs_u2lc, *cp++, 0) != kiconv_char (cs_u2lc, *un++, 0)
+			|| *cp++)
+		{
 			return -1;
+		}
 	}
 	for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) {
 		if (--unlen < 0) {
@@ -703,8 +621,11 @@
 				return chksum;
 			return -1;
 		}
-		if (u2l[*cp++] != u2l[*un++] || *cp++)
+		if (kiconv_char (cs_u2lc, *cp++, 0) != kiconv_char (cs_u2lc, *un++, 0)
+			|| *cp++)
+		{
 			return -1;
+		}
 	}
 	for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) {
 		if (--unlen < 0) {
@@ -712,8 +633,11 @@
 				return chksum;
 			return -1;
 		}
-		if (u2l[*cp++] != u2l[*un++] || *cp++)
+		if (kiconv_char (cs_u2lc, *cp++, 0) != kiconv_char (cs_u2lc, *un++, 0)
+			|| *cp++)
+		{
 			return -1;
+		}
 	}
 	return chksum;
 }
Index: sys/fs/msdosfs/msdosfs_kiconv.c
===================================================================
RCS file: sys/fs/msdosfs/msdosfs_kiconv.c
diff -N sys/fs/msdosfs/msdosfs_kiconv.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/fs/msdosfs/msdosfs_kiconv.c	21 May 2007 18:05:42 -0000
@@ -0,0 +1,456 @@
+#ifdef _KERNEL
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/null.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <fs/msdosfs/msdosfs_kiconv.h>
+//#include <lib/libkern/libkern.h>
+#include <sys/malloc.h>
+#define KICONV_MALLOC(size, type, flags) malloc (size, type, flags)
+#define KICONV_FREE(size, type) free (size, type)
+#else
+#define KICONV_MALLOC(size, type, flags) malloc (size)
+#define KICONV_FREE(ptr, type) free (ptr)
+#endif
+
+typedef struct kiconv_data_ {
+	const int *table;
+} kiconv_data;
+
+static const int
+tr__cp850_to_iso88591 [256] = {
+	0x0,   0x1,   0x2,   0x3,   0x4,   0x5,   0x6,   0x7,
+	0x8,   0x9,   0xA,   0xB,   0xC,   0xD,   0xE,   0xF,
+	0x10,  0x11,  0x12,  0x13,  0x14,  0x15,  0x16,  0x17,
+	0x18,  0x19,  0x1A,  0x1B,  0x1C,  0x1D,  0x1E,  0x1F,
+	' ',   '!',   '"',   '#',   '$',   '%',   '&',  '\'',
+	'(',   ')',   '*',   '+',   ',',   '-',   '.',   '/',
+	'0',   '1',   '2',   '3',   '4',   '5',   '6',   '7',
+	'8',   '9',   ':',   ';',   '<',   '=',   '>',   '?',
+	'@',   'A',   'B',   'C',   'D',   'E',   'F',   'G',
+	'H',   'I',   'J',   'K',   'L',   'M',   'N',   'O',
+	'P',   'Q',   'R',   'S',   'T',   'U',   'V',   'W',
+	'X',   'Y',   'Z',   '[',  0x5C,   ']',   '^',   '_',
+	'`',   'a',   'b',   'c',   'd',   'e',   'f',   'g',
+	'h',   'i',   'j',   'k',   'l',   'm',   'n',   'o',
+	'p',   'q',   'r',   's',   't',   'u',   'v',   'w',
+	'x',   'y',   'z',   '{',   '|',   '}',   '~',  0x7F,
+	0xC7,  0xFC,  0xE9,  0xE2,  0xE4,  0xE0,  0xE5,  0xE7,
+	0xEA,  0xEB,  0xE8,  0xEF,  0xEE,  0xEC,  0xC4,  0xC5,
+	0xC9,  0xE6,  0xC6,  0xF4,  0xF6,  0xF2,  0xFB,  0xF9,
+	0xFF,  0xD6,  0xDC,  0xF8,  0xA3,  0xD8,  0xD7,    -1,
+	0xE1,  0xED,  0xF3,  0xFA,  0xF1,  0xD1,  0xAA,  0xBA,
+	0xBF,  0xAE,  0xAC,  0xBD,  0xBC,  0xA1,  0xAB,  0xBB,
+	-1,    -1,    -1,    -1,    -1,  0xC1,  0xC2,  0xC0,
+	0xA9,    -1,    -1,    -1,    -1,  0xA2,  0xA5,    -1,
+	-1,    -1,    -1,    -1,    -1,    -1,  0xE3,  0xC3,
+	-1,    -1,    -1,    -1,    -1,    -1,    -1,  0xA4,
+	0xF0,  0xD0,  0xCA,  0xCB,  0xC8,    -1,  0xCD,  0xCE,
+	0xCF,    -1,    -1,    -1,    -1,  0xA6,  0xCC,    -1,
+	0xD3,  0xDF,  0xD4,  0xD2,  0xF5,  0xD5,  0xB5,  0xFE,
+	0xDE,  0xDA,  0xDB,  0xD9,  0xFD,  0xDD,  0xAF,  0xB4,
+	0xAD,  0xB1,    -1,  0xBE,  0xB6,  0xA7,  0xF7,  0xB8,
+	0xB0,  0xA8,  0xB7,  0xB9,  0xB3,  0xB2,    -1,  0xA0,
+};
+
+static const int
+tr__iso88591_to_cp850 [256] = {
+	 0x0,   0x1,   0x2,   0x3,   0x4,   0x5,   0x6,   0x7,
+	 0x8,   0x9,   0xA,   0xB,   0xC,   0xD,   0xE,   0xF,
+	0x10,  0x11,  0x12,  0x13,  0x14,  0x15,  0x16,  0x17,
+	0x18,  0x19,  0x1A,  0x1B,  0x1C,  0x1D,  0x1E,  0x1F,
+	 ' ',   '!',   '"',   '#',   '$',   '%',   '&',  '\'',
+	 '(',   ')',   '*',   '+',   ',',   '-',   '.',   '/',
+	 '0',   '1',   '2',   '3',   '4',   '5',   '6',   '7',
+	 '8',   '9',   ':',   ';',   '<',   '=',   '>',   '?',
+	 '@',   'A',   'B',   'C',   'D',   'E',   'F',   'G',
+	 'H',   'I',   'J',   'K',   'L',   'M',   'N',   'O',
+	 'P',   'Q',   'R',   'S',   'T',   'U',   'V',   'W',
+	 'X',   'Y',   'Z',   '[',  0x5C,   ']',   '^',   '_',
+	 '`',   'a',   'b',   'c',   'd',   'e',   'f',   'g',
+	 'h',   'i',   'j',   'k',   'l',   'm',   'n',   'o',
+	 'p',   'q',   'r',   's',   't',   'u',   'v',   'w',
+	 'x',   'y',   'z',   '{',   '|',   '}',   '~',  0x7F,
+	  -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+	  -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+	  -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+	  -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+	0xFF,  0xAD,  0xBD,  0x9C,  0xCF,  0xBE,  0xDD,  0xF5,
+	0xF9,  0xB8,  0xA6,  0xAE,  0xAA,  0xF0,  0xA9,  0xEE,
+	0xF8,  0xF1,  0xFD,  0xFC,  0xEF,  0xE6,  0xF4,  0xFA,
+	0xF7,  0xFB,  0xA7,  0xAF,  0xAC,  0xAB,  0xF3,  0xA8,
+	0xB7,  0xB5,  0xB6,  0xC7,  0x8E,  0x8F,  0x92,  0x80,
+	0xD4,  0x90,  0xD2,  0xD3,  0xDE,  0xD6,  0xD7,  0xD8,
+	0xD1,  0xA5,  0xE3,  0xE0,  0xE2,  0xE5,  0x99,  0x9E,
+	0x9D,  0xEB,  0xE9,  0xEA,  0x9A,  0xED,  0xE8,  0xE1,
+	0x85,  0xA0,  0x83,  0xC6,  0x84,  0x86,  0x91,  0x87,
+	0x8A,  0x82,  0x88,  0x89,  0x8D,  0xA1,  0x8C,  0x8B,
+	0xD0,  0xA4,  0x95,  0xA2,  0x93,  0xE4,  0x94,  0xF6,
+	0x9B,  0x97,  0xA3,  0x96,  0x81,  0xEC,  0xE7,  0x98,
+};
+
+static const int
+tr__cp850_to_upper [256] = {
+	0x00,  0x01,  0x02,  0x03,  0x04,  0x05,  0x06,  0x07,
+	0x08,  0x09,  0x0A,  0x0B,  0x0C,  0x0D,  0x0E,  0x0F,
+	0x10,  0x11,  0x12,  0x13,  0x14,  0x15,  0x16,  0x17,
+	0x18,  0x19,  0x1A,  0x1B,  0x1C,  0x1D,  0x1E,  0x1F,
+	 ' ',   '!',   '"',   '#',   '$',   '%',   '&',  '\'',
+	 '(',   ')',   '*',   '+',   ',',   '-',   '.',   '/',
+	 '0',   '1',   '2',   '3',   '4',   '5',   '6',   '7',
+	 '8',   '9',   ':',   ';',   '<',   '=',   '>',   '?',
+	 '@',   'A',   'B',   'C',   'D',   'E',   'F',   'G',
+	 'H',   'I',   'J',   'K',   'L',   'M',   'N',   'O',
+	 'P',   'Q',   'R',   'S',   'T',   'U',   'V',   'W',
+	 'X',   'Y',   'Z',   '[',  0x5C,   ']',   '^',   '_',
+	 '`',   'A',   'B',   'C',   'D',   'E',   'F',   'G',
+	 'H',   'I',   'J',   'K',   'L',   'M',   'N',   'O',
+	 'P',   'Q',   'R',   'S',   'T',   'U',   'V',   'W',
+	 'X',   'Y',   'Z',   '{',   '|',   '}',   '~',  0x7F,
+	0x80,  0x81,  0x82,  0x83,  0x84,  0x85,  0x86,  0x87,
+	0x88,  0x89,  0x8A,  0x8B,  0x8C,  0x8D,  0x8E,  0x8F,
+	0x90,  0x91,  0x92,  0x93,  0x94,  0x95,  0x96,  0x97,
+	0x98,  0x99,  0x9A,  0x9B,  0x9C,  0x9D,  0x9E,  0x9F,
+	0xA0,  0xA1,  0xA2,  0xA3,  0xA4,  0xA5,  0xA6,  0xA7,
+	0xA8,  0xA9,  0xAA,  0xAB,  0xAC,  0xAD,  0xAE,  0xAF,
+	0xB0,  0xB1,  0xB2,  0xB3,  0xB4,  0xB5,  0xB6,  0xB7,
+	0xB8,  0xB9,  0xBA,  0xBB,  0xBC,  0xBD,  0xBE,  0xBF,
+	0xC0,  0xC1,  0xC2,  0xC3,  0xC4,  0xC5,  0xC6,  0xC7,
+	0xC8,  0xC9,  0xCA,  0xCB,  0xCC,  0xCD,  0xCE,  0xCF,
+	0xD0,  0xD1,  0xD2,  0xD3,  0xD4,  0xD5,  0xD6,  0xD7,
+	0xD8,  0xD9,  0xDA,  0xDB,  0xDC,  0xDD,  0xDE,  0xDF,
+	0xE0,  0xE1,  0xE2,  0xE3,  0xE4,  0xE5,  0xE6,  0xE7,
+	0xE8,  0xE9,  0xEA,  0xEB,  0xEC,  0xED,  0xEE,  0xEF,
+	0xF0,  0xF1,  0xF2,  0xF3,  0xF4,  0xF5,  0xF6,  0xF7,
+	0xF8,  0xF9,  0xFA,  0xFB,  0xFC,  0xFD,  0xFE,  0xFF,
+};
+
+static const int
+tr__iso88591_to_lower [256] = {
+	  0,    0x1,   0x2,   0x3,   0x4,   0x5,   0x6,   0x7,
+	 0x8,   0x9,   0xA,   0xB,   0xC,   0xD,   0xE,   0xF,
+	0x10,  0x11,  0x12,  0x13,  0x14,  0x15,  0x16,  0x17,
+	0x18,  0x19,  0x1A,  0x1B,  0x1C,  0x1D,  0x1E,  0x1F,
+	 ' ',   '!',   '"',   '#',   '$',   '%',   '&',  '\'',
+	 '(',   ')',   '*',   '+',   ',',   '-',   '.',   '/',
+	 '0',   '1',   '2',   '3',   '4',   '5',   '6',   '7',
+	 '8',   '9',   ':',   ';',   '<',   '=',   '>',   '?',
+	 '@',   'a',   'b',   'c',   'd',   'e',   'f',   'g',
+	 'h',   'i',   'j',   'k',   'l',   'm',   'n',   'o',
+	 'p',   'q',   'r',   's',   't',   'u',   'v',   'w',
+	 'x',   'y',   'z',   '[',  0x5C,   ']',   '^',   '_',
+	 '`',   'a',   'b',   'c',   'd',   'e',   'f',   'g',
+	 'h',   'i',   'j',   'k',   'l',   'm',   'n',   'o',
+	 'p',   'q',   'r',   's',   't',   'u',   'v',   'w',
+	 'x',   'y',   'z',   '{',   '|',   '}',   '~',  0x7F,
+	0x80,  0x81,  0x82,  0x83,  0x84,  0x85,  0x86,  0x87,
+	0x88,  0x89,  0x8A,  0x8B,  0x8C,  0x8D,  0x8E,  0x8F,
+	0x90,  0x91,  0x92,  0x93,  0x94,  0x95,  0x96,  0x97,
+	0x98,  0x99,  0x9A,  0x9B,  0x9C,  0x9D,  0x9E,  0x9F,
+	0xA0,  0xA1,  0xA2,  0xA3,  0xA4,  0xA5,  0xA6,  0xA7,
+	0xA8,  0xA9,  0xAA,  0xAB,  0xAC,  0xAD,  0xAE,  0xAF,
+	0xB0,  0xB1,  0xB2,  0xB3,  0xB4,  0xB5,  0xB6,  0xB7,
+	0xB8,  0xB9,  0xBA,  0xBB,  0xBC,  0xBD,  0xBE,  0xBF,
+	0xE0,  0xE1,  0xE2,  0xE3,  0xE4,  0xE5,  0xE6,  0xE7,
+	0xE8,  0xE9,  0xEA,  0xEB,  0xEC,  0xED,  0xEE,  0xEF,
+	0xF0,  0xF1,  0xF2,  0xF3,  0xF4,  0xF5,  0xF6,  0xD7,
+	0xF8,  0xF9,  0xFA,  0xFB,  0xFC,  0xFD,  0xFE,  0xDF,
+	0xE0,  0xE1,  0xE2,  0xE3,  0xE4,  0xE5,  0xE6,  0xE7,
+	0xE8,  0xE9,  0xEA,  0xEB,  0xEC,  0xED,  0xEE,  0xEF,
+	0xF0,  0xF1,  0xF2,  0xF3,  0xF4,  0xF5,  0xF6,  0xF7,
+	0xF8,  0xF9,  0xFA,  0xFB,  0xFC,  0xFD,  0xFE,  0xFF,
+};
+
+static const int
+tr__cp866_to_cp1251 [256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+	' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',
+	'(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+	'0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+	'8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+	'@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
+	'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+	'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
+	'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',
+	'`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+	'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+	'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+	'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7F,
+	0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+	0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+	0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
+	0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+	0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+	0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+	0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+	0xA8, 0xB8, 0xAA, 0xBA, 0xAF, 0xBF, 0xA1, 0xA2,
+	0xB0,   -1, 0xB7,   -1, 0xB9, 0xA4,   -1, 0xA0,
+};
+
+static const int
+tr__cp1251_to_cp866 [256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+	' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',
+	'(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+	'0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+	'8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+	'@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
+	'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+	'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
+	'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',
+	'`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+	'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+	'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+	'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7F,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	-1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+	0xFF, 0xF6, 0xF7,   -1, 0xFD,   -1,   -1,   -1,
+	0xF0,   -1, 0xF2,   -1,   -1,   -1,   -1, 0xF4,
+	0xF8,   -1,   -1,   -1,   -1,   -1,   -1, 0xFA,
+	0xF1, 0xFC, 0xF3,   -1,   -1,   -1,   -1, 0xF5,
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+	0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+	0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+	0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+	0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+	0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+};
+
+static const int
+tr__cp866_to_upper [256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+	' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',
+	'(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+	'0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+	'8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+	'@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
+	'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+	'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
+	'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',
+	'`',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
+	'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+	'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
+	'X',  'Y',  'Z',  '{',  '|',  '}',  '~', 0x7F,
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+	0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+	0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
+	0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+	0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+	0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+	0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
+	0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+	0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+	0xF0, 0xF0, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+	0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+};
+
+static const int
+tr__cp1251_to_lower [256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+	' ',  '!',  '"',  '#',  '$',  '%',  '&', '\'',
+	'(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+	'0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+	'8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+	'@',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+	'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+	'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+	'x',  'y',  'z',  '[', '\\',  ']',  '^',  '_',
+	'`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+	'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+	'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+	'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7F,
+	0x90, 0x83, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x9A, 0x8B, 0x9C, 0x9D, 0x9E, 0x9F,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+	0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+	0xA0, 0xA2, 0xA2, 0xBC, 0xA4, 0xB4, 0xA6, 0xA7,
+	0xB8, 0xA9, 0xBA, 0xAB, 0xAC, 0xAD, 0xAE, 0xBF,
+	0xB0, 0xB1, 0xB3, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
+	0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBE, 0xBE, 0xBF,
+	0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+	0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+	0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+	0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+	0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+	0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+	0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+	0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+};
+
+void * kiconv_open (const char *dstname, const char *srcname)
+{
+	static const char s_CP1251    [] = "cp1251";
+	static const char s_CP866     [] = "cp866";
+	static const char s_ISO88591  [] = "iso-8859-1";
+	static const char s_CP850     [] = "cp850";
+
+	static struct {
+		const char *s;
+		const char *d;
+		const int *t;
+	} conv_table [] = {
+		{s_CP1251,   KICONV_TOLOWER, tr__cp1251_to_lower},
+		{s_CP866,    KICONV_TOUPPER, tr__cp866_to_upper},
+
+		{s_CP1251,   s_CP866,        tr__cp1251_to_cp866},
+		{s_CP866,    s_CP1251,       tr__cp866_to_cp1251},
+
+		{s_ISO88591, KICONV_TOLOWER, tr__iso88591_to_lower},
+		{s_CP850,    KICONV_TOUPPER, tr__cp850_to_upper},
+
+		{s_CP850,    s_ISO88591,     tr__cp850_to_iso88591},
+		{s_ISO88591, s_CP850,        tr__iso88591_to_cp850},
+	};
+
+	int i;
+
+	printf ("kiconv_open: before malloc\n");
+
+	kiconv_data *ret = KICONV_MALLOC (sizeof (kiconv_data), M_TEMP, M_WAITOK);
+
+	printf ("kiconv_open: after malloc %p\n", ret);
+
+	if (!ret)
+		return NULL;
+
+	printf ("kiconv_open: before strcmp\n");
+	printf ("kiconv_open: before strcmp dst=%s\n", dstname);
+	printf ("kiconv_open: before strcmp src=%s\n", srcname);
+
+	for (i=0; i < sizeof (conv_table)/sizeof (conv_table [0]); ++i){
+		if (!strcmp (conv_table [i].s, srcname) &&
+		    !strcmp (conv_table [i].d, dstname))
+		{
+			ret -> table = conv_table [i].t;
+			return ret;
+		}
+	}
+
+	printf ("kiconv_open: NULL!!!\n");
+/* Uncomment the following line!
+	KICONV_FREE (ret -> table, M_TEMP);
+*/
+	return NULL;
+}
+
+int kiconv_close (void * descr)
+{
+	if (!descr){
+		/* NULL must be allowed */
+		return 0;
+	}
+
+	/* actual code here */
+	return 0;
+}
+
+size_t kiconv (void * descr, const char ** src, size_t * srcleft,
+			   char ** dst, size_t * dstleft)
+{
+	size_t srcl, dstl;
+	const char *srcp;
+	char *dstp;
+	int s, d;
+	size_t ret;
+
+	srcp = *src;
+	dstp = *dst;
+	srcl = *srcleft;
+	dstl = *dstleft;
+
+	ret = 0;
+
+	while (srcl && dstl){
+		s = (unsigned char) *srcp;
+		d = ((const kiconv_data *) descr) -> table [s];
+
+		if (d == 0 && s != 0){
+			ret = (size_t) -1;
+			break;
+		}
+
+		*dstp = d;
+
+		++srcp;
+		--srcl;
+		++dstp;
+		--dstl;
+	}
+
+	*src = srcp;
+	*dst = dstp;
+	*srcleft = srcl;
+	*dstleft = dstl;
+	return ret;
+}
+
+void kiconv_string (
+	void * descr,
+	const char *src, size_t src_len,
+	char *dst, size_t dst_max_len,
+	char error_char)
+{
+	/* assert (max_dst_len > 0); */
+
+	if (src_len == (size_t) -1)
+		src_len = strlen (src) + 1;
+
+	while (0 != kiconv (descr, &src, &src_len, &dst, &dst_max_len)){
+		*dst++ = error_char;
+		--dst_max_len;
+
+		++src;
+		--src_len;
+	}
+}
+
+char
+kiconv_char (void * descr, char c, char error_char)
+{
+	size_t sl;
+	size_t dl;
+	const char *s;
+	char *d;
+	size_t err_cnt;
+
+	sl = dl = 1;
+	s = &c;
+	d = &c;
+
+	err_cnt = kiconv (descr, &s, &sl, &d, &dl);
+
+	if (err_cnt)
+		return error_char;
+	else
+		return c;
+}
Index: sys/fs/msdosfs/msdosfs_kiconv.h
===================================================================
RCS file: sys/fs/msdosfs/msdosfs_kiconv.h
diff -N sys/fs/msdosfs/msdosfs_kiconv.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/fs/msdosfs/msdosfs_kiconv.h	21 May 2007 18:05:42 -0000
@@ -0,0 +1,29 @@
+#ifndef _KICONV_H_
+#define _KICONV_H_
+
+#define KICONV_TOUPPER ("toupper")
+#define KICONV_TOLOWER ("tolower")
+
+/* iconv(3) like API */
+void *
+kiconv_open (const char *dstname, const char *srcname);
+
+int
+kiconv_close (void * descr);
+
+size_t
+kiconv (void * descr, const char ** src, size_t * srcleft,
+		char ** dst, size_t * dstleft);
+
+/* extrac funcs */
+void
+kiconv_string (
+	void * descr,
+	const char *src, size_t src_len,
+	char *dst, size_t dst_max_len,
+	char error_char);
+
+char
+kiconv_char (void * descr, char c, char error_char);
+
+#endif // _KICONV_H_
Index: sys/fs/msdosfs/msdosfs_vfsops.c
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfs_vfsops.c,v
retrieving revision 1.40.2.1
diff -u -r1.40.2.1 msdosfs_vfsops.c
--- sys/fs/msdosfs/msdosfs_vfsops.c	27 Feb 2007 23:11:36 -0000	1.40.2.1
+++ sys/fs/msdosfs/msdosfs_vfsops.c	21 May 2007 18:05:47 -0000
@@ -76,6 +76,7 @@
 #include <sys/conf.h>
 #include <sys/kauth.h>
 
+#include <fs/msdosfs/msdosfs_kiconv.h>
 #include <fs/msdosfs/bpb.h>
 #include <fs/msdosfs/bootsect.h>
 #include <fs/msdosfs/direntry.h>
@@ -107,6 +108,11 @@
 MALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOS FS mount structure");
 MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS fat", "MSDOS FS fat table");
 
+void *cs_u2d;
+void *cs_d2u;
+void *cs_d2uc;
+void *cs_u2lc;
+
 #define ROOTNAME "root_device"
 
 extern const struct vnodeopv_desc msdosfs_vnodeop_opv_desc;
@@ -140,6 +146,54 @@
 };
 VFS_ATTACH(msdosfs_vfsops);
 
+static
+int update_mp_kiconv (
+	struct msdosfs_args *argp,
+	struct msdosfsmount *pmp)
+{
+	int error;
+
+	if (!argp->cs_unix [0]){
+		strlcpy (argp->cs_unix, MSDOSFSMNT_CS_UNIX, sizeof (argp->cs_unix));
+	}
+	if (!argp->cs_dos [0]){
+		strlcpy (argp->cs_dos, MSDOSFSMNT_CS_DOS, sizeof (argp->cs_dos));
+	}
+
+	printf ("msdosfs_mount: cs_unix=%p\n", argp->cs_unix);
+	printf ("msdosfs_mount: cs_unix=%s\n", argp->cs_unix);
+
+	printf ("msdosfs_mount: cs_dos=%p\n", argp->cs_dos);
+	printf ("msdosfs_mount: cs_dos=%s\n", argp->cs_dos);
+
+
+	error = 0;
+	printf ("msdosfs_mount: I'm before kiconv_open\n");
+
+	cs_u2d  = pmp->pm_cs_u2d  = kiconv_open (argp->cs_dos,  argp->cs_unix);
+	cs_d2u  = pmp->pm_cs_d2u  = kiconv_open (argp->cs_unix, argp->cs_dos);
+	cs_u2lc = pmp->pm_cs_u2lc = kiconv_open (KICONV_TOLOWER, argp->cs_unix);
+	cs_d2uc = pmp->pm_cs_d2uc = kiconv_open (KICONV_TOUPPER, argp->cs_dos);
+
+	printf ("msdosfs_mount: I'm after kiconv_open\n");
+
+	if (!cs_u2d || !cs_d2u || !cs_u2lc || !cs_d2uc){
+		kiconv_close (cs_u2d);
+		kiconv_close (cs_d2u);
+		kiconv_close (cs_u2lc);
+		kiconv_close (cs_d2uc);
+
+		error = EFAULT;
+		goto ex;
+	}
+
+	printf ("msdosfs_mount: kiconv_open succeded\n");
+
+ ex:
+	printf ("msdosfs_mount: error code=%d\n", error);
+	return error;
+}
+
 static int
 update_mp(mp, argp)
 	struct mount *mp;
@@ -148,6 +202,11 @@
 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
 	int error;
 
+	printf ("msdosfs_mount: I'm inside update_mp\n");
+	error = update_mp_kiconv (argp, pmp);
+	if (error)
+		return error;
+
 	pmp->pm_gid = argp->gid;
 	pmp->pm_uid = argp->uid;
 	pmp->pm_mask = argp->mask & ALLPERMS;
@@ -844,6 +903,11 @@
 		    ((u_int *)vp->v_data)[1]);
 	}
 #endif
+	kiconv_close (pmp->pm_cs_d2u);
+	kiconv_close (pmp->pm_cs_u2d);
+	kiconv_close (pmp->pm_cs_d2uc);
+	kiconv_close (pmp->pm_cs_u2lc);
+
 	vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
 	error = VOP_CLOSE(pmp->pm_devvp,
 	    pmp->pm_flags & MSDOSFSMNT_RONLY ? FREAD : FREAD|FWRITE, NOCRED, l);
Index: sys/fs/msdosfs/msdosfsmount.h
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfsmount.h,v
retrieving revision 1.12
diff -u -r1.12 msdosfsmount.h
--- sys/fs/msdosfs/msdosfsmount.h	25 Nov 2006 12:17:30 -0000	1.12
+++ sys/fs/msdosfs/msdosfsmount.h	21 May 2007 18:05:47 -0000
@@ -50,9 +50,15 @@
 #ifndef _MSDOSFS_MSDOSFSMOUNT_H_
 #define _MSDOSFS_MSDOSFSMOUNT_H_
 
+/* default charsets */
+#define MSDOSFSMNT_CS_DOS  "cp850"
+#define MSDOSFSMNT_CS_UNIX "iso-8859-1" /* "utf-8" */
+
 /*
  *  Arguments to mount MSDOS filesystems.
  */
+#define MSDOSFSMNT_MAXCSLEN 32
+
 struct msdosfs_args {
 	char	*fspec;		/* blocks special holding the fs to mount */
 	struct	export_args30 _pad1; /* compat with old userland tools */
@@ -66,6 +72,9 @@
 #define MSDOSFSMNT_VERSION	3
 	mode_t  dirmask;	/* v2: mask to be applied for msdosfs perms */
 	int	gmtoff;		/* v3: offset from UTC in seconds */
+	/* Character sets */
+	char    cs_dos  [MSDOSFSMNT_MAXCSLEN];
+	char    cs_unix [MSDOSFSMNT_MAXCSLEN];
 };
 
 /*
@@ -133,6 +142,11 @@
 	u_int pm_curfat;	/* current fat for FAT32 (0 otherwise) */
 	u_int *pm_inusemap;	/* ptr to bitmap of in-use clusters */
 	u_int pm_flags;		/* see below */
+	/* Character sets convertors */
+	void *pm_cs_d2u;    /* DOS to Unix */
+	void *pm_cs_u2d;    /* Unix to DOS */
+	void *pm_cs_u2lc;   /* UNIX to lower case */
+	void *pm_cs_d2uc;   /* DOS to upper case */
 };
 /* Byte offset in FAT on filesystem pmp, cluster cn */
 #define	FATOFS(pmp, cn)	((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv)
Index: sys/lkm/vfs/msdosfs/Makefile
===================================================================
RCS file: /cvsroot/src/sys/lkm/vfs/msdosfs/Makefile,v
retrieving revision 1.10
diff -u -r1.10 Makefile
--- sys/lkm/vfs/msdosfs/Makefile	26 Dec 2002 12:31:25 -0000	1.10
+++ sys/lkm/vfs/msdosfs/Makefile	21 May 2007 18:05:47 -0000
@@ -8,6 +8,6 @@
 
 SRCS=	lkminit_vfs.c
 SRCS+=	msdosfs_conv.c msdosfs_denode.c msdosfs_fat.c msdosfs_lookup.c
-SRCS+=	msdosfs_vfsops.c msdosfs_vnops.c
+SRCS+=	msdosfs_vfsops.c msdosfs_vnops.c msdosfs_kiconv.c
 
 .include <bsd.kmod.mk>