--- old/usr/src/lib/libc/port/locale/setlocale.c	Sun Jun  8 22:09:56 2014
+++ new/usr/src/lib/libc/port/locale/setlocale.c	Sun Jun  8 22:09:56 2014
@@ -1,4 +1,5 @@
 /*
+ * Copyright 2014 Garrett D'Amore <garrett@damore.org>
  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1996 - 2002 FreeBSD Project
  * Copyright (c) 1991, 1993
@@ -41,194 +42,72 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <alloca.h>
 #include <stdio.h>
 #include "collate.h"
-#include "lmonetary.h"	/* for __monetary_load_locale() */
-#include "lnumeric.h"	/* for __numeric_load_locale() */
-#include "lmessages.h"	/* for __messages_load_locale() */
+#include "lnumeric.h"	/* for struct lc_numeric */
+#include "lctype.h"	/* for struct lc_ctype */
 #include "setlocale.h"
-#include "ldpart.h"
-#include "timelocal.h" /* for __time_load_locale() */
 #include "../i18n/_loc_path.h"
+#include "localeimpl.h"
+#include "../i18n/_locale.h"
 
 /*
- * Category names for getenv()  Note that this was modified
- * for Solaris.  See <iso/locale_iso.h>.
- */
-#define	NUM_CATS	7
-static char *categories[7] = {
-	"LC_CTYPE",
-	"LC_NUMERIC",
-	"LC_TIME",
-	"LC_COLLATE",
-	"LC_MONETARY",
-	"LC_MESSAGES",
-	"LC_ALL",
-};
-
-/*
- * Current locales for each category
- */
-static char current_categories[NUM_CATS][ENCODING_LEN + 1] = {
-	"C",
-	"C",
-	"C",
-	"C",
-	"C",
-	"C",
-	"C",
-};
-
-/*
  * Path to locale storage directory.  See ../i18n/_loc_path.h
  */
 char	*_PathLocale = _DFLT_LOC_PATH;
 
-/*
- * The locales we are going to try and load
- */
-static char new_categories[NUM_CATS][ENCODING_LEN + 1];
-static char saved_categories[NUM_CATS][ENCODING_LEN + 1];
-static char current_locale_string[NUM_CATS * (ENCODING_LEN + 1 + 1)];
+static char	*current_locale(locale_t, int);
+static void	install_legacy(locale_t, int);
 
-static char	*currentlocale(void);
-static char	*loadlocale(int);
-static const char *__get_locale_env(int);
-
 char *
-setlocale(int category, const char *locale)
+setlocale(int category, const char *locname)
 {
-	int i, j, saverr;
-	const char *env, *r;
+	locale_t oldloc;
+	locale_t newloc;
+	int mask;
 
-	if (category < 0 || category >= NUM_CATS) {
+	if (category < 0 || category > LC_ALL) {
 		errno = EINVAL;
 		return (NULL);
 	}
 
-	if (locale == NULL)
-		return (category != LC_ALL ?
-		    current_categories[category] : currentlocale());
+	if (locname == NULL)
+		return (current_locale(___global_locale, category));
 
-	/*
-	 * Default to the current locale for everything.
-	 */
-	for (i = 0; i < NUM_CATS; ++i)
-		(void) strcpy(new_categories[i], current_categories[i]);
+	if ((oldloc = duplocale(___global_locale)) == NULL)
+		return (NULL);
 
-	/*
-	 * Now go fill up new_categories from the locale argument
-	 */
-	if (!*locale) {
-		if (category == LC_ALL) {
-			for (i = 0; i < NUM_CATS; ++i) {
-				if (i == LC_ALL)
-					continue;
-				env = __get_locale_env(i);
-				if (strlen(env) > ENCODING_LEN) {
-					errno = EINVAL;
-					return (NULL);
-				}
-				(void) strcpy(new_categories[i], env);
-			}
-		} else {
-			env = __get_locale_env(category);
-			if (strlen(env) > ENCODING_LEN) {
-				errno = EINVAL;
-				return (NULL);
-			}
-			(void) strcpy(new_categories[category], env);
-		}
-	} else if (category != LC_ALL) {
-		if (strlen(locale) > ENCODING_LEN) {
-			errno = EINVAL;
-			return (NULL);
-		}
-		(void) strcpy(new_categories[category], locale);
-	} else {
-		if ((r = strchr(locale, '/')) == NULL) {
-			if (strlen(locale) > ENCODING_LEN) {
-				errno = EINVAL;
-				return (NULL);
-			}
-			for (i = 0; i < NUM_CATS; ++i)
-				(void) strcpy(new_categories[i], locale);
-		} else {
-			char	*buf;
-			char	*save;
+	mask = (category == LC_ALL ? LC_ALL_MASK : (1 << category));
 
-			buf = alloca(strlen(locale) + 1);
-			(void) strcpy(buf, locale);
-
-			save = NULL;
-			r = strtok_r(buf, "/", &save);
-			for (i = 0;  i < NUM_CATS; i++) {
-				if (i == LC_ALL)
-					continue;
-				if (r == NULL) {
-					/*
-					 * Composite Locale is inadequately
-					 * specified!   (Or with empty fields.)
-					 * The old code would fill fields
-					 * out from the last one, but I think
-					 * this is suboptimal.
-					 */
-					errno = EINVAL;
-					return (NULL);
-				}
-				(void) strlcpy(new_categories[i], r,
-				    ENCODING_LEN);
-				r = strtok_r(NULL, "/", &save);
-			}
-			if (r != NULL) {
-				/*
-				 * Too many components - we had left over
-				 * data in the LC_ALL.  It is malformed.
-				 */
-				errno = EINVAL;
-				return (NULL);
-			}
-		}
+	newloc = newlocale(mask, locname, oldloc);
+	if (newloc == NULL) {
+		freelocale(oldloc);
+		return (NULL);
 	}
 
-	if (category != LC_ALL)
-		return (loadlocale(category));
+	oldloc = ___global_locale;
+	___global_locale = newloc;
 
-	for (i = 0; i < NUM_CATS; ++i) {
-		(void) strcpy(saved_categories[i], current_categories[i]);
-		if (i == LC_ALL)
-			continue;
-		if (loadlocale(i) == NULL) {
-			saverr = errno;
-			for (j = 0; j < i; j++) {
-				(void) strcpy(new_categories[j],
-				    saved_categories[j]);
-				if (i == LC_ALL)
-					continue;
-				if (loadlocale(j) == NULL) {
-					(void) strcpy(new_categories[j], "C");
-					(void) loadlocale(j);
-				}
-			}
-			errno = saverr;
-			return (NULL);
-		}
-	}
-	return (currentlocale());
+	install_legacy(newloc, mask);
+
+	freelocale(oldloc);
+	return (current_locale(newloc, category));
 }
 
 static char *
-currentlocale(void)
+current_locale(locale_t loc, int cat)
 {
-	int i;
 	int composite = 0;
+	static char buf[LC_ALL * (ENCODING_LEN + 1 + 1)];
 
+	if (cat != LC_ALL) {
+		return (loc->locdata[cat]->l_lname);
+	}
+
 	/* Look to see if any category is different */
-	for (i = 1; i < NUM_CATS; ++i) {
-		if (i == LC_ALL)
-			continue;
-		if (strcmp(current_categories[0], current_categories[i])) {
+	for (int i = 1; i < LC_ALL; ++i) {
+		if (strcmp(loc->locdata[0]->l_lname,
+		    loc->locdata[i]->l_lname) != 0) {
 			composite = 1;
 			break;
 		}
@@ -240,90 +119,63 @@
 		 * if the order is changed, then setlocale() will need
 		 * to be changed as well.
 		 */
-		(void) snprintf(current_locale_string,
-		    sizeof (current_locale_string),
+		(void) snprintf(buf, sizeof (buf),
 		    "%s/%s/%s/%s/%s/%s",
-		    current_categories[LC_CTYPE],
-		    current_categories[LC_NUMERIC],
-		    current_categories[LC_TIME],
-		    current_categories[LC_COLLATE],
-		    current_categories[LC_MONETARY],
-		    current_categories[LC_MESSAGES]);
-	} else {
-		(void) strlcpy(current_locale_string, current_categories[0],
-		    sizeof (current_locale_string));
+		    loc->locdata[LC_CTYPE]->l_lname,
+		    loc->locdata[LC_NUMERIC]->l_lname,
+		    loc->locdata[LC_TIME]->l_lname,
+		    loc->locdata[LC_COLLATE]->l_lname,
+		    loc->locdata[LC_MONETARY]->l_lname,
+		    loc->locdata[LC_MESSAGES]->l_lname);
+		return (buf);
 	}
-	return (current_locale_string);
+
+	return (loc->locdata[0]->l_lname);
 }
 
-static char *
-loadlocale(int category)
+static void
+install_legacy(locale_t loc, int mask)
 {
-	char *new = new_categories[category];
-	char *old = current_categories[category];
-	int (*func)(const char *);
-
-	if ((new[0] == '.' &&
-	    (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) ||
-	    strchr(new, '/') != NULL) {
-		errno = EINVAL;
-		return (NULL);
+	/*
+	 * Update the legacy fixed variables that may be baked into
+	 * legacy programs.  This is really unfortunate, but we can't
+	 * solve for them otherwise.  Note that such legacy programs
+	 * are only going to see the global locale settings, and cannot
+	 * benefit from uselocale().
+	 */
+	if (mask & LC_NUMERIC_MASK) {
+		struct lc_numeric *lnum;
+		lnum = loc->locdata[LC_NUMERIC]->l_data[0];
+		_numeric[0] = *lnum->decimal_point;
+		_numeric[1] = *lnum->thousands_sep;
 	}
 
-	switch (category) {
-	case LC_CTYPE:
-		func = __wrap_setrunelocale;
-		break;
-	case LC_COLLATE:
-		func = _collate_load_tables;
-		break;
-	case LC_TIME:
-		func = __time_load_locale;
-		break;
-	case LC_NUMERIC:
-		func = __numeric_load_locale;
-		break;
-	case LC_MONETARY:
-		func = __monetary_load_locale;
-		break;
-	case LC_MESSAGES:
-		func = __messages_load_locale;
-		break;
-	default:
-		errno = EINVAL;
-		return (NULL);
-	}
+	if (mask & LC_CTYPE_MASK) {
+		struct lc_ctype *lct;
+		lct = loc->locdata[LC_CTYPE]->l_data[0];
+		for (int i = 0; i < _CACHED_RUNES; i++) {
+			/* ctype can only encode the lower 8 bits. */
+			__ctype[i+1] = lct->lc_ctype_mask[i] & 0xff;
+			__ctype_mask[i] = lct->lc_ctype_mask[i];
+		}
 
-	if (strcmp(new, old) == 0)
-		return (old);
+		/* The bottom half is the toupper/lower array */
+		for (int i = 0; i < _CACHED_RUNES; i++) {
+			int u, l;
+			__ctype[258 + i] = i;
+			u = lct->lc_trans_upper[i];
+			l = lct->lc_trans_lower[i];
+			if (u && u != i)
+				__ctype[258+i] = u;
+			if (l && l != i)
+				__ctype[258+i] = l;
 
-	if (func(new) != _LDP_ERROR) {
-		(void) strcpy(old, new);
-		return (old);
-	}
+			/* Don't forget these annoyances either! */
+			__trans_upper[i] = u;
+			__trans_lower[i] = l;
+		}
 
-	return (NULL);
-}
-
-static const char *
-__get_locale_env(int category)
-{
-	const char *env;
-
-	/* 1. check LC_ALL. */
-	env = getenv(categories[LC_ALL]);
-
-	/* 2. check LC_* */
-	if (env == NULL || !*env)
-		env = getenv(categories[category]);
-
-	/* 3. check LANG */
-	if (env == NULL || !*env)
-		env = getenv("LANG");
-
-	/* 4. if none is set, fall to "C" */
-	if (env == NULL || !*env)
-		env = "C";
-
-	return (env);
+		/* Maximum mblen, cswidth, weird legacy */
+		__ctype[520] = lct->lc_max_mblen;
+	}
 }
