Index: sys/msdosfs/direntry.h
===================================================================
RCS file: /cvsroot/syssrc/sys/msdosfs/direntry.h,v
retrieving revision 1.14
diff -u -r1.14 direntry.h
--- sys/msdosfs/direntry.h	1997/11/17 15:36:32	1.14
+++ sys/msdosfs/direntry.h	2002/10/14 07:59:04
@@ -122,14 +122,18 @@
 void	unix2dostime __P((struct timespec *tsp, u_int16_t *ddp,
 	    u_int16_t *dtp, u_int8_t *dhp));
 void	dos2unixtime __P((u_int dd, u_int dt, u_int dh, struct timespec *tsp));
-int	dos2unixfn __P((u_char dn[11], u_char *un, int lower));
+int	dos2unixfn __P((u_char dn[11], u_char *un, int lower, int d2u_loaded,
+	    u_int8_t *d2u, int ul_loaded, u_int8_t *ul));
 int	unix2dosfn __P((const u_char *un, u_char dn[12], int unlen,
-	    u_int gen));
+	    u_int gen, int u2d_loaded, u_int8_t *u2d, int lu_loaded,
+	    u_int8_t *lu));
 int	unix2winfn __P((const u_char *un, int unlen, struct winentry *wep,
-	    int cnt, int chksum));
+	    int cnt, int chksum, int table_loaded, u_int16_t *u2w));
 int	winChkName __P((const u_char *un, int unlen, struct winentry *wep,
-	    int chksum));
-int	win2unixfn __P((struct winentry *wep, struct dirent *dp, int chksum));
+	    int chksum, int u2w_loaded, u_int16_t *u2w, int ul_loaded,
+	    u_int8_t *ul));
+int	win2unixfn __P((struct winentry *wep, struct dirent *dp, int chksum,
+	    int table_loaded, u_int16_t *u2w));
 u_int8_t winChksum __P((u_int8_t *name));
 int	winSlotCnt __P((const u_char *un, int unlen));
 #endif	/* _KERNEL */
Index: sys/msdosfs/msdosfs_conv.c
===================================================================
RCS file: /cvsroot/syssrc/sys/msdosfs/msdosfs_conv.c,v
retrieving revision 1.32
diff -u -r1.32 msdosfs_conv.c
--- sys/msdosfs/msdosfs_conv.c	2002/01/08 20:44:13	1.32
+++ sys/msdosfs/msdosfs_conv.c	2002/10/14 07:59:05
@@ -1,4 +1,4 @@
-/*	$NetBSD: msdosfs_conv.c,v 1.32 2002/01/08 20:44:13 jdolecek Exp $	*/
+/*	$NetBSD$ */
 
 /*-
  * Copyright (C) 1995, 1997 Wolfgang Solfrank.
@@ -86,11 +86,13 @@
  * Variables used to remember parts of the last time conversion.  Maybe we
  * can avoid a full conversion.
  */
-u_long lasttime;
-u_long lastday;
-u_short lastddate;
-u_short lastdtime;
+static u_long  lasttime;
+static u_long  lastday;
+static u_short lastddate;
+static u_short lastdtime;
 
+static inline u_int8_t find_lcode __P((u_int16_t code, u_int16_t *u2w));
+
 /*
  * 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.
@@ -340,6 +342,42 @@
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */
 };
 
+static u_char
+l2u[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 */
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */
+	0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 40-47 */
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 48-4f */
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 50-57 */
+	0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 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
@@ -353,12 +391,16 @@
  * null.
  */
 int
-dos2unixfn(dn, un, lower)
+dos2unixfn(dn, un, lower, d2u_loaded, d2u, ul_loaded, ul)
 	u_char dn[11];
 	u_char *un;
 	int lower;
+	int d2u_loaded;
+	u_int8_t *d2u;
+	int ul_loaded;
+	u_int8_t *ul;
 {
-	int i, j;
+	int i;
 	int thislong = 1;
 	u_char c;
 
@@ -369,26 +411,27 @@
 	 * directory slot. Another dos quirk.
 	 */
 	if (*dn == SLOT_E5)
-		c = dos2unix[0xe5];
+		c = d2u_loaded ? d2u[0xe5 & 0x7f] : dos2unix[0xe5];
 	else
-		c = dos2unix[*dn];
-	*un++ = lower ? u2l[c] : c;
+		c = d2u_loaded && (*dn & 0x80) ? d2u[*dn & 0x7f] :
+		    dos2unix[*dn];
+	*un++ = lower ? (ul_loaded && (c & 0x80) ?
+			 ul[c & 0x7f] : u2l[c]) : c;
+	dn++;
 
 	/*
-	 * Copy the rest into the unix filename string, ignoring
-	 * trailing blanks.
-	 */
-
-	for (j=7; (j >= 0) && (dn[j] == ' '); j--)
-		;
-
-	for (i = 1; i <= j; i++) {
-		c = dos2unix[dn[i]];
-		*un++ = lower ? u2l[c] : c;
+	 * Copy the name portion into the unix filename string.
+	 */
+	for (i = 1; i < 8 && *dn != ' '; i++) {
+		c = d2u_loaded && (*dn & 0x80) ? d2u[*dn & 0x7f] :
+		    dos2unix[*dn];
+		dn++;
+		*un++ = lower ? (ul_loaded && (c & 0x80) ?
+				 ul[c & 0x7f] : u2l[c]) : c;
 		thislong++;
 	}
-	dn += 8;
-	
+	dn += 8 - i;
+
 	/*
 	 * Now, if there is an extension then put in a period and copy in
 	 * the extension.
@@ -397,8 +440,11 @@
 		*un++ = '.';
 		thislong++;
 		for (i = 0; i < 3 && *dn != ' '; i++) {
-			c = dos2unix[*dn++];
-			*un++ = lower ? u2l[c] : c;
+			c = d2u_loaded && (*dn & 0x80) ? d2u[*dn & 0x7f] :
+			    dos2unix[*dn];
+			dn++;
+			*un++ = lower ? (ul_loaded && (c & 0x80) ?
+					 ul[c & 0x7f] : u2l[c]) : c;
 			thislong++;
 		}
 	}
@@ -419,17 +465,21 @@
  *	3 if conversion was successful and generation number was inserted
  */
 int
-unix2dosfn(un, dn, unlen, gen)
+unix2dosfn(un, dn, unlen, gen, u2d_loaded, u2d, lu_loaded, lu)
 	const u_char *un;
 	u_char dn[12];
 	int unlen;
 	u_int gen;
+	int u2d_loaded;
+	u_int8_t *u2d;
+	int lu_loaded;
+	u_int8_t *lu;
 {
 	int i, j, l;
 	int conv = 1;
 	const u_char *cp, *dp, *dp1;
 	u_char gentext[6], *wcp;
-	int shortlen;
+	u_int8_t c;
 
 	/*
 	 * Fill the dos filename string with blanks. These are DOS's pad
@@ -493,7 +543,12 @@
 		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 = dp[i];
+			c = lu_loaded && (c & 0x80) ?
+			    lu[c & 0x7f] : l2u[c];
+			c = u2d_loaded && (c & 0x80) ?
+			    u2d[c & 0x7f] : unix2dos[c];
+			if (dp[i] != (dn[j] = c)
 			    && conv != 3)
 				conv = 2;
 			if (!dn[j]) {
@@ -509,17 +564,15 @@
 		dp++;
 	}
 
-	shortlen = (dp - un) <= 8;
-
 	/*
 	 * Now convert the rest of the name
 	 */
 	for (i = j = 0; un < dp && j < 8; i++, j++, un++) {
-		if ((*un == ' ') && shortlen)
-			dn[j] = ' ';
-		else
-			dn[j] = unix2dos[*un];
-		if ((*un != dn[j])
+		c = lu_loaded && (*un & 0x80) ?
+		    lu[*un & 0x7f] : l2u[*un];
+		c = u2d_loaded && (c & 0x80) ?
+		    u2d[c & 0x7f] : unix2dos[c];
+		if (*un != (dn[j] = c)
 		    && conv != 3)
 			conv = 2;
 		if (!dn[j]) {
@@ -576,16 +629,19 @@
  *	 i.e. doesn't consist solely of blanks and dots
  */
 int
-unix2winfn(un, unlen, wep, cnt, chksum)
+unix2winfn(un, unlen, wep, cnt, chksum, table_loaded, u2w)
 	const u_char *un;
 	int unlen;
 	struct winentry *wep;
 	int cnt;
 	int chksum;
+	int table_loaded;
+	u_int16_t *u2w;
 {
 	const u_int8_t *cp;
 	u_int8_t *wcp;
 	int i;
+	u_int16_t code;
 
 	/*
 	 * Drop trailing blanks and dots
@@ -611,20 +667,38 @@
 	for (wcp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) {
 		if (--unlen < 0)
 			goto done;
-		*wcp++ = *un++;
-		*wcp++ = 0;
+		if (table_loaded && (*un & 0x80)) {
+			code = u2w[*un++ & 0x7f];
+			*wcp++ = code;
+			*wcp++ = code >> 8;
+		} else {
+			*wcp++ = *un++;
+			*wcp++ = 0;
+		}
 	}
 	for (wcp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) {
 		if (--unlen < 0)
 			goto done;
-		*wcp++ = *un++;
-		*wcp++ = 0;
+		if (table_loaded && (*un & 0x80)) {
+			code = u2w[*un++ & 0x7f];
+			*wcp++ = code;
+			*wcp++ = code >> 8;
+		} else {
+			*wcp++ = *un++;
+			*wcp++ = 0;
+		}
 	}
 	for (wcp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) {
 		if (--unlen < 0)
 			goto done;
-		*wcp++ = *un++;
-		*wcp++ = 0;
+		if (table_loaded && (*un & 0x80)) {
+			code = u2w[*un++ & 0x7f];
+			*wcp++ = code;
+			*wcp++ = code >> 8;
+		} else {
+			*wcp++ = *un++;
+			*wcp++ = 0;
+		}
 	}
 	if (!unlen)
 		wep->weCnt |= WIN_LAST;
@@ -637,19 +711,38 @@
 	return 0;
 }
 
+static inline u_int8_t
+find_lcode(code, u2w)
+	u_int16_t code;
+	u_int16_t *u2w;
+{
+	int i;
+
+	for (i = 0; i < 128; i++)
+		if (u2w[i] == code)
+			return (i | 0x80);
+	return '?';
+}
+
 /*
  * Compare our filename to the one in the Win95 entry
  * Returns the checksum or -1 if no match
  */
 int
-winChkName(un, unlen, wep, chksum)
+winChkName(un, unlen, wep, chksum, u2w_loaded, u2w, ul_loaded, ul)
 	const u_char *un;
 	int unlen;
 	struct winentry *wep;
 	int chksum;
+	int u2w_loaded;
+	u_int16_t *u2w;
+	int ul_loaded;
+	u_int8_t *ul;
 {
 	u_int8_t *cp;
 	int i;
+	u_int16_t code;
+	u_int8_t c1, c2;
 
 	/*
 	 * First compare checksums
@@ -693,8 +786,21 @@
 				return chksum;
 			return -1;
 		}
-		if (u2l[*cp++] != u2l[*un++] || *cp++)
+		code = (cp[1] << 8) | cp[0];
+		if (code & 0xff80) {
+			if (u2w_loaded)
+				code = find_lcode(code, u2w);
+			else if (code & 0xff00)
+				code = '?';
+		}
+		c1 = ul_loaded && (code & 0x80) ?
+		     ul[code & 0x7f] : u2l[code];
+		c2 = ul_loaded && (*un & 0x80) ?
+		     ul[*un & 0x7f] : u2l[*un];
+		if (c1 != c2)
 			return -1;
+		cp += 2;
+		un++;
 	}
 	for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) {
 		if (--unlen < 0) {
@@ -702,8 +808,21 @@
 				return chksum;
 			return -1;
 		}
-		if (u2l[*cp++] != u2l[*un++] || *cp++)
+		code = (cp[1] << 8) | cp[0];
+		if (code & 0xff80) {
+			if (u2w_loaded)
+				code = find_lcode(code, u2w);
+			else if (code & 0xff00)
+				code = '?';
+		}
+		c1 = ul_loaded && (code & 0x80) ?
+		     ul[code & 0x7f] : u2l[code];
+		c2 = ul_loaded && (*un & 0x80) ?
+		     ul[*un & 0x7f] : u2l[*un];
+		if (c1 != c2)
 			return -1;
+		cp += 2;
+		un++;
 	}
 	for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) {
 		if (--unlen < 0) {
@@ -711,8 +830,21 @@
 				return chksum;
 			return -1;
 		}
-		if (u2l[*cp++] != u2l[*un++] || *cp++)
+		code = (cp[1] << 8) | cp[0];
+		if (code & 0xff80) {
+			if (u2w_loaded)
+				code = find_lcode(code, u2w);
+			else if (code & 0xff00)
+				code = '?';
+		}
+		c1 = ul_loaded && (code & 0x80) ?
+		     ul[code & 0x7f] : u2l[code];
+		c2 = ul_loaded && (*un & 0x80) ?
+		     ul[*un & 0x7f] : u2l[*un];
+		if (c1 != c2)
 			return -1;
+		cp += 2;
+		un++;
 	}
 	return chksum;
 }
@@ -722,13 +854,16 @@
  * Returns the checksum or -1 if impossible
  */
 int
-win2unixfn(wep, dp, chksum)
+win2unixfn(wep, dp, chksum, table_loaded, u2w)
 	struct winentry *wep;
 	struct dirent *dp;
 	int chksum;
+	int table_loaded;
+	u_int16_t *u2w;
 {
 	u_int8_t *cp;
 	u_int8_t *np, *ep = dp->d_name + WIN_MAXLEN;
+	u_int16_t code;
 	int i;
 
 	if ((wep->weCnt&WIN_CNT) > howmany(WIN_MAXLEN, WIN_CHARS)
@@ -759,14 +894,25 @@
 	 * Convert the name parts
 	 */
 	for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) {
-		switch (*np++ = *cp++) {
+		code = (cp[1] << 8) | cp[0];
+		switch (code) {
 		case 0:
+			*np = '\0';
 			dp->d_namlen -= sizeof(wep->wePart2)/2
 			    + sizeof(wep->wePart3)/2 + i + 1;
 			return chksum;
 		case '/':
-			np[-1] = 0;
+			*np = '\0';
 			return -1;
+		default:
+			if (code & 0xff80) {
+				if (table_loaded)
+					code = find_lcode(code, u2w);
+				else if (code & 0xff00)
+					code = '?';
+			}
+			*np++ = code;
+			break;
 		}
 		/*
 		 * The size comparison should result in the compiler
@@ -777,17 +923,27 @@
 			np[-1] = 0;
 			return -1;
 		}
-		if (*cp++)
-			return -1;
+		cp += 2;
 	}
 	for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) {
-		switch (*np++ = *cp++) {
+		code = (cp[1] << 8) | cp[0];
+		switch (code) {
 		case 0:
+			*np = '\0';
 			dp->d_namlen -= sizeof(wep->wePart3)/2 + i + 1;
 			return chksum;
 		case '/':
-			np[-1] = 0;
+			*np = '\0';
 			return -1;
+		default:
+			if (code & 0xff80) {
+				if (table_loaded)
+					code = find_lcode(code, u2w);
+				else if (code & 0xff00)
+					code = '?';
+			}
+			*np++ = code;
+			break;
 		}
 		/*
 		 * The size comparisons should be optimized away
@@ -798,17 +954,27 @@
 			np[-1] = 0;
 			return -1;
 		}
-		if (*cp++)
-			return -1;
+		cp += 2;
 	}
 	for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) {
-		switch (*np++ = *cp++) {
+		code = (cp[1] << 8) | cp[0];
+		switch (code) {
 		case 0:
+			*np = '\0';
 			dp->d_namlen -= i + 1;
 			return chksum;
 		case '/':
-			np[-1] = 0;
+			*np = '\0';
 			return -1;
+		default:
+			if (code & 0xff80) {
+				if (table_loaded)
+					code = find_lcode(code, u2w);
+				else if (code & 0xff00)
+					code = '?';
+			}
+			*np++ = code;
+			break;
 		}
 		/*
 		 * See above
@@ -818,8 +984,7 @@
 			np[-1] = 0;
 			return -1;
 		}
-		if (*cp++)
-			return -1;
+		cp += 2;
 	}
 	return chksum;
 }
Index: sys/msdosfs/msdosfs_lookup.c
===================================================================
RCS file: /cvsroot/syssrc/sys/msdosfs/msdosfs_lookup.c,v
retrieving revision 1.52
diff -u -r1.52 msdosfs_lookup.c
--- sys/msdosfs/msdosfs_lookup.c	2001/11/10 13:26:45	1.52
+++ sys/msdosfs/msdosfs_lookup.c	2002/10/14 07:59:05
@@ -173,7 +173,9 @@
 	}
 
 	switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,
-	    cnp->cn_namelen, 0)) {
+	    cnp->cn_namelen, 0,
+	    pmp->pm_flags & MSDOSFSMNT_U2WTABLE, pmp->pm_u2d,
+	    pmp->pm_flags & MSDOSFSMNT_ULTABLE, pmp->pm_lu)) {
 	case 0:
 		return (EINVAL);
 	case 1:
@@ -275,7 +277,11 @@
 					chksum = winChkName((const u_char *)cnp->cn_nameptr,
 							    cnp->cn_namelen,
 							    (struct winentry *)dep,
-							    chksum);
+							    chksum,
+							    pmp->pm_flags & MSDOSFSMNT_U2WTABLE,
+							    pmp->pm_u2w,
+							    pmp->pm_flags & MSDOSFSMNT_ULTABLE,
+							    pmp->pm_ul);
 					continue;
 				}
 
@@ -697,7 +703,9 @@
 				fndoffset -= sizeof(struct direntry);
 			}
 			if (!unix2winfn(un, unlen, (struct winentry *)ndep,
-						wcnt, chksum))
+					wcnt, chksum,
+					pmp->pm_flags & MSDOSFSMNT_U2WTABLE,
+					pmp->pm_u2w))
 				break;
 		}
 	}
@@ -1097,7 +1105,9 @@
 		 * Generate DOS name with generation number
 		 */
 		if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
-		    cnp->cn_namelen, gen))
+		    cnp->cn_namelen, gen,
+		    pmp->pm_flags & MSDOSFSMNT_U2WTABLE, pmp->pm_u2d,
+		    pmp->pm_flags & MSDOSFSMNT_ULTABLE, pmp->pm_lu))
 			return gen == 1 ? EINVAL : EEXIST;
 
 		/*
Index: sys/msdosfs/msdosfs_vfsops.c
===================================================================
RCS file: /cvsroot/syssrc/sys/msdosfs/msdosfs_vfsops.c,v
retrieving revision 1.84
diff -u -r1.84 msdosfs_vfsops.c
--- sys/msdosfs/msdosfs_vfsops.c	2002/09/21 18:13:25	1.84
+++ sys/msdosfs/msdosfs_vfsops.c	2002/10/14 07:59:05
@@ -144,6 +144,15 @@
 	pmp->pm_uid = argp->uid;
 	pmp->pm_mask = argp->mask & ALLPERMS;
 	pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT;
+	if (pmp->pm_flags & MSDOSFSMNT_U2WTABLE) {
+		memcpy(pmp->pm_u2w, argp->u2w, sizeof(pmp->pm_u2w));
+		memcpy(pmp->pm_d2u, argp->d2u, sizeof(pmp->pm_d2u));
+		memcpy(pmp->pm_u2d, argp->u2d, sizeof(pmp->pm_u2d));
+	}
+	if (pmp->pm_flags & MSDOSFSMNT_ULTABLE) {
+		memcpy(pmp->pm_ul, argp->ul, sizeof(pmp->pm_ul));
+		memcpy(pmp->pm_lu, argp->lu, sizeof(pmp->pm_lu));
+	}
 
 	/*
 	 * GEMDOS knows nothing (yet) about win95
Index: sys/msdosfs/msdosfs_vnops.c
===================================================================
RCS file: /cvsroot/syssrc/sys/msdosfs/msdosfs_vnops.c,v
retrieving revision 1.122
diff -u -r1.122 msdosfs_vnops.c
--- sys/msdosfs/msdosfs_vnops.c	2002/09/27 15:37:49	1.122
+++ sys/msdosfs/msdosfs_vnops.c	2002/10/14 07:59:06
@@ -1557,7 +1557,10 @@
 			if (dentp->deAttributes == ATTR_WIN95) {
 				if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
 					continue;
-				chksum = win2unixfn((struct winentry *)dentp, &dirbuf, chksum);
+				chksum = win2unixfn((struct winentry *)dentp,
+					&dirbuf, chksum,
+					pmp->pm_flags & MSDOSFSMNT_U2WTABLE,
+					pmp->pm_u2w);
 				continue;
 			}
 
@@ -1596,7 +1599,11 @@
 			if (chksum != winChksum(dentp->deName))
 				dirbuf.d_namlen = dos2unixfn(dentp->deName,
 				    (u_char *)dirbuf.d_name,
-				    pmp->pm_flags & MSDOSFSMNT_SHORTNAME);
+				    pmp->pm_flags & MSDOSFSMNT_SHORTNAME,
+				    pmp->pm_flags & MSDOSFSMNT_U2WTABLE,
+				    pmp->pm_d2u,
+				    pmp->pm_flags & MSDOSFSMNT_ULTABLE,
+				    pmp->pm_ul);
 			else
 				dirbuf.d_name[dirbuf.d_namlen] = 0;
 			chksum = -1;
Index: sys/msdosfs/msdosfsmount.h
===================================================================
RCS file: /cvsroot/syssrc/sys/msdosfs/msdosfsmount.h,v
retrieving revision 1.23
diff -u -r1.23 msdosfsmount.h
--- sys/msdosfs/msdosfsmount.h	2002/09/21 18:13:26	1.23
+++ sys/msdosfs/msdosfsmount.h	2002/10/14 07:59:06
@@ -57,6 +57,11 @@
 	gid_t	gid;		/* gid that owns msdosfs files */
 	mode_t  mask;		/* mask to be applied for msdosfs perms */
 	int	flags;		/* see below */
+	u_int16_t u2w[128];     /* Local->Unicode table */
+	u_int8_t  ul[128];      /* Local upper->lower table */
+	u_int8_t  lu[128];      /* Local lower->upper table */
+	u_int8_t  d2u[128];     /* DOS->local table */
+	u_int8_t  u2d[128];     /* Local->DOS table */
 };
 
 /*
@@ -66,11 +71,14 @@
 #define	MSDOSFSMNT_LONGNAME	2	/* Force Win'95 long names */
 #define	MSDOSFSMNT_NOWIN95	4	/* Completely ignore Win95 entries */
 #define	MSDOSFSMNT_GEMDOSFS	8	/* This is a gemdos-flavour */
+#define MSDOSFSMNT_U2WTABLE     0x10    /* Local->Unicode and local<->DOS   */
+					/* tables loaded                    */
+#define MSDOSFSMNT_ULTABLE      0x20    /* Local upper<->lower table loaded */
 
 /* All flags above: */
 #define	MSDOSFSMNT_MNTOPT \
 	(MSDOSFSMNT_SHORTNAME|MSDOSFSMNT_LONGNAME|MSDOSFSMNT_NOWIN95 \
-	 |MSDOSFSMNT_GEMDOSFS)
+	 |MSDOSFSMNT_GEMDOSFS|MSDOSFSMNT_U2WTABLE|MSDOSFSMNT_ULTABLE)
 #define	MSDOSFSMNT_RONLY	0x80000000	/* mounted read-only	*/
 #define	MSDOSFSMNT_WAITONFAT	0x40000000	/* mounted synchronous	*/
 #define	MSDOSFS_FATMIRROR	0x20000000	/* FAT is mirrored */
@@ -116,6 +124,11 @@
 	u_int *pm_inusemap;	/* ptr to bitmap of in-use clusters */
 	u_int pm_flags;		/* see below */
 	struct netexport pm_export;	/* export information */
+	u_int16_t pm_u2w[128];  /* Local->Unicode table */
+	u_int8_t  pm_ul[128];   /* Local upper->lower table */
+	u_int8_t  pm_lu[128];   /* Local lower->upper table */
+	u_int8_t  pm_d2u[128];  /* DOS->local table */
+	u_int8_t  pm_u2d[128];  /* Local->DOS table */
 };
 /* Byte offset in FAT on filesystem pmp, cluster cn */
 #define	FATOFS(pmp, cn)	((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv)
Index: sbin/mount_msdos/mount_msdos.8
===================================================================
RCS file: /cvsroot/basesrc/sbin/mount_msdos/mount_msdos.8,v
retrieving revision 1.23
diff -u -r1.23 mount_msdos.8
--- sbin/mount_msdos/mount_msdos.8	2002/10/01 13:40:40	1.23
+++ sbin/mount_msdos/mount_msdos.8	2002/10/14 09:35:15
@@ -32,7 +32,7 @@
 .\"
 .\" <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
 .\"
-.Dd April 7, 1994
+.Dd October 14, 2002
 .Dt MOUNT_MSDOS 8
 .Os
 .Sh NAME
@@ -48,6 +48,8 @@
 .Op Fl l
 .Op Fl 9
 .Op Fl G
+.Op Fl L Ar locale
+.Op Fl W Ar table
 .Pa special
 .Pa node
 .Sh DESCRIPTION
@@ -135,6 +137,45 @@
 limited to the boot block.
 This option enforces
 .Fl s .
+.It Fl L Ar locale
+Specify locale name used for internal uppercase and lowercase conversions
+for DOS and Win'95 names.
+By default ISO 8859-1 assumed as local character set.
+.It Fl W Ar table
+Specify text file with 3 conversion tables:
+.Bl -enum
+.It
+Local character set to Unicode conversion table (upper half) for Win'95 long
+names, 128 Unicode codes separated by 8 per row.
+If some code not present in Unicode, use
+0x003F code ('?') as replacement.
+.It
+DOS to local character set conversion table (upper half) for DOS names,
+128 character codes separated by 8 per row.
+Code 0x3F ('?') used for impossible translations.
+.It
+Local character set to DOS conversion table (upper half) for DOS names,
+128 character codes separated by 8 per row.
+Some codes have special meaning:
+.Bl -hang
+.It 0x00
+character disallowed in DOS file name;
+.It 0x01
+character should be replaced by '_' in DOS file name;
+.It 0x02
+character should be skipped in DOS file name;
+.El
+.El
+.Pp
+By default ISO 8859-1 assumed as local character set.
+If file path isn't absolute,
+.Pa /usr/libdata/msdosfs/
+prefix prepended.
+.El
+.Sh FILES
+.Bl -tag -width /usr/libdata/msdosfs -compact
+.It Pa /usr/libdata/msdosfs
+default place for character sets conversion tables
 .El
 .Sh SEE ALSO
 .Xr mount 2 ,
Index: sbin/mount_msdos/mount_msdos.c
===================================================================
RCS file: /cvsroot/basesrc/sbin/mount_msdos/mount_msdos.c,v
retrieving revision 1.25
diff -u -r1.25 mount_msdos.c
--- sbin/mount_msdos/mount_msdos.c	2002/09/21 18:43:35	1.25
+++ sbin/mount_msdos/mount_msdos.c	2002/10/14 09:35:16
@@ -47,6 +47,7 @@
 #include <ctype.h>
 #include <err.h>
 #include <grp.h>
+#include <locale.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -68,6 +69,8 @@
 int	main __P((int, char *[]));
 int	mount_msdos __P((int argc, char **argv));
 static void	usage __P((void));
+static void	load_u2wtable __P((struct msdosfs_args *, char *));
+static void	load_ultable __P((struct msdosfs_args *, char *));
 
 #ifndef MOUNT_NOMAIN
 int
@@ -92,7 +95,7 @@
 	mntflags = set_gid = set_uid = set_mask = 0;
 	(void)memset(&args, '\0', sizeof(args));
 
-	while ((c = getopt(argc, argv, "Gsl9u:g:m:o:")) != -1) {
+	while ((c = getopt(argc, argv, "Gsl9u:g:m:o:L:W:")) != -1) {
 		switch (c) {
 		case 'G':
 			args.flags |= MSDOSFSMNT_GEMDOSFS;
@@ -118,6 +121,14 @@
 			args.mask = a_mask(optarg);
 			set_mask = 1;
 			break;
+		case 'L':
+			load_ultable(&args, optarg);
+			args.flags |= MSDOSFSMNT_ULTABLE;
+			break;
+		case 'W':
+			load_u2wtable(&args, optarg);
+			args.flags |= MSDOSFSMNT_U2WTABLE;
+			break;
 		case 'o':
 			getmntopts(optarg, mopts, &mntflags, 0);
 			break;
@@ -178,6 +189,89 @@
 usage()
 {
 
-	fprintf(stderr, "usage: mount_msdos [-o options] [-u user] [-g group] [-m mask] bdev dir\n");
+	fprintf(stderr, "usage: mount_msdos [-o options] [-u user] [-g group] "
+	    "[-m mask] [-s] [-l] [-9] [-G] [-L locale] [-W table] bdev dir\n");
 	exit(1);
+}
+
+void
+load_u2wtable (pargs, name)
+	struct msdosfs_args *pargs;
+	char *name;
+{
+	FILE *f;
+	int i, j, code[8];
+	size_t line = 0;
+	char buf[MAXPATHLEN+1];
+	char *fn, *s, *p;
+
+	if (*name == '/')
+		fn = name;
+	else {
+		snprintf(buf, sizeof(buf), "/usr/libdata/msdosfs/%s", name);
+		buf[MAXPATHLEN+1] = '\0';
+		fn = buf;
+	}
+	if ((f = fopen(fn, "r")) == NULL)
+		err(EXIT_FAILURE, "%s", fn);
+	p = NULL;
+	for (i = 0; i < 16; i++) {
+		do {
+			if (p != NULL) free(p);
+			if ((p = s = fparseln(f, NULL, &line, NULL, 0)) == NULL)
+				errx(EXIT_FAILURE, "can't read u2w table row %d near line %d", i, line);
+			while (isspace((unsigned char)*s))
+				s++;
+		} while (*s == '\0');
+		if (sscanf(s, "%i%i%i%i%i%i%i%i",
+code, code + 1, code + 2, code + 3, code + 4, code + 5, code + 6, code + 7) != 8)
+			errx(EXIT_FAILURE, "u2w table: missing item(s) in row %d, line %d", i, line);
+		for (j = 0; j < 8; j++)
+			pargs->u2w[i * 8 + j] = code[j];
+	}
+	for (i = 0; i < 16; i++) {
+		do {
+			free(p);
+			if ((p = s = fparseln(f, NULL, &line, NULL, 0)) == NULL)
+				errx(EXIT_FAILURE, "can't read d2u table row %d near line %d", i, line);
+			while (isspace((unsigned char)*s))
+				s++;
+		} while (*s == '\0');
+		if (sscanf(s, "%i%i%i%i%i%i%i%i",
+code, code + 1, code + 2, code + 3, code + 4, code + 5, code + 6, code + 7) != 8)
+			errx(EXIT_FAILURE, "d2u table: missing item(s) in row %d, line %d", i, line);
+		for (j = 0; j < 8; j++)
+			pargs->d2u[i * 8 + j] = code[j];
+	}
+	for (i = 0; i < 16; i++) {
+		do {
+			free(p);
+			if ((p = s = fparseln(f, NULL, &line, NULL, 0)) == NULL)
+				errx(EXIT_FAILURE, "can't read u2d table row %d near line %d", i, line);
+			while (isspace((unsigned char)*s))
+				s++;
+		} while (*s == '\0');
+		if (sscanf(s, "%i%i%i%i%i%i%i%i",
+code, code + 1, code + 2, code + 3, code + 4, code + 5, code + 6, code + 7) != 8)
+			errx(EXIT_FAILURE, "u2d table: missing item(s) in row %d, line %d", i, line);
+		for (j = 0; j < 8; j++)
+			pargs->u2d[i * 8 + j] = code[j];
+	}
+	free(p);
+	fclose(f);
+}
+
+void
+load_ultable (pargs, name)
+	struct msdosfs_args *pargs;
+	char *name;
+{
+	int i;
+
+	if (setlocale(LC_CTYPE, name) == NULL)
+		errx(EXIT_FAILURE, "%s: Unsupported locale", name);
+	for (i = 0; i < 128; i++) {
+		pargs->ul[i] = tolower(i | 0x80);
+		pargs->lu[i] = toupper(i | 0x80);
+	}
 }
