usr/src/lib/liborchestrator/locale.c
changeset 0 87f703f8362e
child 26 fb9b1fd08e73
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/liborchestrator/locale.c	Tue Nov 13 16:48:11 2007 -0800
@@ -0,0 +1,1982 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"@(#)locale.c	1.3	07/08/27 SMI"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <sys/kbio.h>
+#include <stdio.h>
+#include <libintl.h>
+#include <locale.h>
+#include <malloc.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "spmicommon_api.h"
+#include "orchestrator_private.h"
+#include "orchestrator_lang_codes.h"
+
+#define	COUNTRY_SEP	'_'
+#define	CODESET_SEP	'.'
+#define	UTF		"UTF-8"
+
+#define	SIMPLIFIED_CHINESE	"Chinese-Simplified"
+#define	TRADITIONAL_CHINESE	"Chinese-Traditional"
+
+#define	INSTALL_NLS_PATH	"/usr/lib/install/data/lib/locale"
+#define	NLS_PATH		"/usr/lib/locale"
+
+
+/* Static variables used to store language/locale system information */
+
+static	lang_info_t 	*install_ll_list = NULL;	/* lang, locale list */
+static	lang_info_t	*supported_ll_list = NULL;
+static	char		*install_lang_list[MAX_NUM_LANG];
+static	char		*supported_lang_list[MAX_NUM_LANG];
+static	char		**install_languages = NULL;	/* sorted list */
+static	char		**supported_languages = NULL;
+static	char		*app_locale = NULL;
+static	int		lang_initialized = 0;
+static	int		install_initialized = 0;
+static	int		install_lang_total = 0;
+static	int		supported_lang_total = 0;
+
+struct	chinese_values {
+	char 	*lang;
+	char	*lang_name;
+	char	*lang_code;
+} chinese_values[] = {
+	{"zh", SIMPLIFIED_CHINESE, "sc"},
+	{"zh.GBK", SIMPLIFIED_CHINESE, "sc"},
+	{"zh.UTF-8", SIMPLIFIED_CHINESE, "sc"},
+	{"zh_CN", "SIMPLIFIED_CHINESE", "sc"},
+	{"zh_CN.GB18030", SIMPLIFIED_CHINESE, "sc"},
+	{"zh_HK", TRADITIONAL_CHINESE, "tc"},
+	{"zh_HK.BIG5HK", TRADITIONAL_CHINESE, "tc"},
+	{"zh_HK.UTF-8", TRADITIONAL_CHINESE, "tc"},
+	{"zh_TW", TRADITIONAL_CHINESE, "tc"},
+	{"zh_TW.BIG5", TRADITIONAL_CHINESE, "tc"},
+	{"zh_TW.UTF-8", TRADITIONAL_CHINESE, "tc"}
+};
+
+
+static int 	add_lang_to_list(char ***list, char *locale, int *k, int j);
+static void 	add_locale_entry_to_lang(lang_info_t *lp, char *locale,
+    char *region, boolean_t is_default);
+static int	build_language_list(char *path, char **, int *);
+static void	build_install_ll_list(char *nlspath, char **list,
+    int lang_total, lang_info_t **return_list, int *ll_total);
+static void	build_ll_list(char **list, int lang_total,
+		    lang_info_t **, int *total);
+static	char	*copy_up_to(char *start, char *t);
+static int	create_lang_entry(char *lang, char *locale, char *region,
+    lang_info_t **, boolean_t is_default);
+static void	end_of_comp(char **t, char **start);
+static char 	**get_actual_languages(char **list, int *);
+static lang_info_t *get_lang_entry(char *, lang_info_t *search_list);
+static char 	*get_locale_component(char **t, char **start);
+static char 	*get_locale_description(char *lang, char *region);
+static int 	handle_chinese_language(char *region, char **lang);
+static boolean_t is_locale_in_installer_lang(char *locale_name);
+static boolean_t is_valid_locale(char *locale);
+static int 	list_cmp(const void *p1, const void *p2);
+static int 	lang_init(char *path, char **list, int *total, int *init_var);
+static int 	save_system_default_locale(char *locale);
+static void 	set_lang(char *locale);
+static void 	set_lc(char *, char *, char *, char *, char *, char *);
+static char 	*strip_comment(char *buf);
+static char 	*translate_description(char *locale, char *desc);
+static void 	translate_lang_names(lang_info_t **list);
+static void 	sort_lang_list(char **unsorted_list, int total);
+static char 	*substitute_chinese_language(char *locale, char **code);
+static char 	*substitute_C_POSIX_language(char **code);
+static void 	update_init(FILE *fp, char *locale);
+static void 	update_env(char *locale);
+
+#define	STR_LANG	"LANG="
+#define	LEN_LANG	(sizeof (STR_LANG) - 1)
+#define	STR_LC_COLLATE	"LC_COLLATE="
+#define	LEN_LC_COLLATE	(sizeof (STR_LC_COLLATE) - 1)
+#define	STR_LC_CTYPE	"LC_CTYPE="
+#define	LEN_LC_CTYPE	(sizeof (STR_LC_CTYPE) - 1)
+#define	STR_LC_MESSAGES	"LC_MESSAGES="
+#define	LEN_LC_MESSAGES	(sizeof (STR_LC_MESSAGES) - 1)
+#define	STR_LC_MONETARY	"LC_MONETARY="
+#define	LEN_LC_MONETARY	(sizeof (STR_LC_MONETARY) - 1)
+#define	STR_LC_NUMERIC	"LC_NUMERIC="
+#define	LEN_LC_NUMERIC	(sizeof (STR_LC_NUMERIC) - 1)
+#define	STR_LC_TIME	"LC_TIME="
+#define	LEN_LC_TIME	(sizeof (STR_LC_TIME) - 1)
+
+
+/*
+ * om_get_install_lang_info
+ * This function returns the locales available for use in setting
+ * installer locale.
+ * Input:	None
+ * Output:	int *total, returns the total number of locales found
+ * Return:	Pointer to lang_info_t which is a linked list of
+ *		locales available for selection.
+ *		NULL if no locales found.
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILIRE if no locales found.
+ */
+lang_info_t *
+om_get_install_lang_info(int *total)
+{
+	int		ll_total;
+	int		ret;
+
+	/*
+	 * Path to look for locale data.
+	 */
+	*total = 0;
+	/*
+	 * Build the lang list for the install application support.
+	 */
+	if (!install_initialized) {
+		ret = lang_init(INSTALL_NLS_PATH, (char **)&install_lang_list,
+		    &install_lang_total, &install_initialized);
+		if (ret)
+			return (NULL);
+	}
+
+	/*
+	 * For the install languages we build the locale list associated
+	 * with them by reading the lcctab file in the /usr/lib/locale/
+	 * directory.
+	 */
+	build_install_ll_list(NLS_PATH, (char **)&install_lang_list,
+	    install_lang_total, &install_ll_list, &ll_total);
+	*total = ll_total;
+	return (install_ll_list);
+}
+
+/*
+ * om_get_install_lang_names
+ * This function returns the lang names available for use in setting
+ * installer locale system
+ * Input:	None
+ * Output:	int *total, returns the total number of locales found
+ * Return:	char ** list of lang names available for selection.
+ *		NULL if no locales found.
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILIRE if no locales found.
+ */
+char **
+om_get_install_lang_names(int *total)
+{
+	int		ret;
+	*total = 0;
+
+	/*
+	 * Reads directory that contains installation application
+	 * languages. Fills install_lang_list with this data,
+	 * if this has not been initialized yet.
+	 */
+	if (!install_initialized) {
+		ret = lang_init(INSTALL_NLS_PATH, (char **)&install_lang_list,
+		    &install_lang_total, &install_initialized);
+		if (ret)
+			return (NULL);
+	}
+	install_languages = get_actual_languages((char **)install_lang_list,
+	    total);
+	sort_lang_list(install_languages, *total);
+	return (install_languages);
+}
+
+/*
+ * om_get_lang_info
+ * This function returns the available locales for installation on to
+ * system
+ * Input:	None
+ * Output:	int *total, returns the total number of locales found
+ * Return:	Pointer to lang_info_t which is a linked list of
+ *		locales available for selection to install.
+ *		NULL if no locales found.
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILIRE if no locales found.
+ */
+lang_info_t *
+om_get_lang_info(int *total)
+{
+	int	ret;
+	int	locale_total;
+
+	*total = 0;
+
+	if (!lang_initialized) {
+		ret = lang_init(NLS_PATH, (char **)&supported_lang_list,
+		    &supported_lang_total, &lang_initialized);
+		if (ret)
+			return (NULL);
+	}
+	/*
+	 * This function looks in usr/lib/locale on the installation
+	 * media to determine languages that we provide support for.
+	 */
+	sort_lang_list((char **)&supported_lang_list, supported_lang_total);
+	build_ll_list((char **)&supported_lang_list, supported_lang_total,
+	    &supported_ll_list, &locale_total);
+	*total = locale_total;
+	return (supported_ll_list);
+}
+
+/*
+ * om_get_lang_names
+ * This function returns the language strings that can are supported for
+ * installation on to system.
+ * Input:	None
+ * Output:	int *total, returns the total number of lang names found
+ * Return:	char ** list of lang names
+ *		NULL if no lang names found.
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILURE if no locales found.
+ */
+
+char **
+om_get_lang_names(int *total)
+{
+	int	ret = 0;
+	*total = 0;
+
+	/*
+	 * We only gather 'supported lang names'. Supported means
+	 * full locale, UTF-8 only. For display to the user for
+	 * default locale selection.
+	 */
+	if (!lang_initialized) {
+		ret = lang_init(NLS_PATH, (char **)&supported_lang_list,
+		    &supported_lang_total, &lang_initialized);
+		if (ret) {
+			return (NULL);
+		}
+	}
+	supported_languages = get_actual_languages(supported_lang_list, total);
+	sort_lang_list(supported_languages, *total);
+	return (supported_languages);
+}
+
+/*
+ * om_get_locale_info
+ * This function returns a linked list of locale_info_t's that correspond
+ * to the language specified.
+ * Input:	char *lang - language for which to return locale data.
+ * Output:	int *total, returns the total number of lang names found
+ * Return:	locale_info_t * list of locale_names.
+ *		NULL if no locale names found.
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILURE if no locales found.
+ */
+locale_info_t *
+om_get_locale_info(char *lang, int *total)
+{
+	lang_info_t *langp;
+	locale_info_t *localep = NULL;
+	lang_info_t	*tmp = supported_ll_list;
+
+	if (tmp == NULL)
+		tmp = install_ll_list;
+	if (tmp == NULL)
+		return (NULL);
+
+	/*
+	 * The lang is passed in as the lang code. Not the translated
+	 * name.
+	 */
+	for (langp = tmp; langp != NULL; langp = langp->next) {
+		if (strcmp(langp->lang, lang) == 0) {
+			localep = langp->locale_info;
+			break;
+		}
+	}
+	*total = langp->n_locales;
+	return (localep);
+}
+
+/*
+ * om_get_locale_names
+ * This function returns a list of locale names that correspond
+ * to the language specified.
+ * Input:	char *lang - language for which to return locale data.
+ * Output:	int *total, returns the total number of lang names found
+ * Return:	char ** list of locale_names.
+ *		NULL if no locale names found.
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILURE if no locales found.
+ */
+char **
+om_get_locale_names(char *lang, int *total)
+{
+	lang_info_t 	*langp;
+	locale_info_t 	*localep = NULL;
+	char		**locale_names;
+	int		i = 0;
+
+	/*
+	 * locale_names is a an array of char *. Allocate each array
+	 * member before strduping the locale_info in to this array.
+	 */
+
+	/*
+	 * The lang is passed in as the lang code. Not the translated
+	 * name.
+	 */
+	for (langp = supported_ll_list; langp != NULL; langp = langp->next) {
+		if (strcmp(langp->lang, lang) == 0) {
+			localep = langp->locale_info;
+			break;
+		}
+	}
+	/*
+	 * allocate number of char * pointers to correspond to number
+	 * of locale names.
+	 */
+	locale_names = (char **)malloc(langp->n_locales * sizeof (char *));
+	if (locale_names == NULL) {
+		om_set_error(OM_NO_SPACE);
+		return (NULL);
+	}
+
+	for (; localep; localep = localep->next) {
+		locale_names[i] = strdup(localep->locale_name);
+		if (locale_names[i] == NULL) {
+			om_free_lang_names(locale_names);
+			om_set_error(OM_NO_SPACE);
+			return (NULL);
+		}
+		i++;
+	}
+	*total = i;
+	return (locale_names);
+}
+
+/*
+ * om_set_install_lang_by_value
+ * This function sets the installer application language, using the
+ * lang_info_t * data passed in.
+ * Input:	lang_info_t	* lang/locale info to set
+ * Output:	none
+ * Return:	Success or Failure
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILURE if no locales found.
+ */
+int
+om_set_install_lang_by_value(lang_info_t *localep)
+{
+	/*
+	 * For install applications, there will only be 1 locale
+	 * associated with that language.
+	 */
+	locale_info_t *locp = localep->locale_info;
+	if (locp == NULL) {
+		om_set_error(OM_INVALID_LOCALE);
+		return (OM_FAILURE);
+	}
+
+	om_save_locale(locp->locale_name, B_TRUE);
+	return (OM_SUCCESS);
+}
+
+/*
+ * om_set_install_lang_by_name
+ * This function sets the installer application language, using the
+ * lang name passed in.
+ * Input:	char *lang name to set.
+ * Output:	none
+ * Return:	int, Success or Failure
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILURE if no locales found.
+ */
+int
+om_set_install_lang_by_name(char *lang)
+{
+	locale_info_t	*locp;
+	int		total;
+	/*
+	 * Find the locale entry based on the name passed in. If no
+	 * locales for this language use the default locale of C/POSIX.
+	 */
+	locp = om_get_locale_info(lang, &total);
+
+	if (locp == NULL) {
+		om_set_error(OM_INVALID_LOCALE);
+		return (OM_FAILURE);
+	}
+
+	/*
+	 * Now, set the environment for the installation application.
+	 */
+	om_save_locale(locp->locale_name, B_TRUE);
+	om_free_locale_info(locp);
+	return (OM_SUCCESS);
+}
+
+/*
+ * om_set_default_locale_by_name
+ * This function sets the system default locale by name
+ * Input:	char *lang name to set.
+ * Output:	none
+ * Return:	int, Success or Failure
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILURE if no locales found.
+ */
+int
+om_set_default_locale_by_name(char *localep)
+{
+	int ret = 0;
+
+	/*
+	 * C/Posix is the default, there is no need to specify it
+	 * in the /etc/default/init file.
+	 */
+	if (strcasecmp(localep, "C/Posix") == 0 ||
+	    strcasecmp(localep, "Posix") == 0) {
+		return (ret);
+	}
+	ret = save_system_default_locale(localep);
+	if (!ret)
+		om_save_locale(localep, B_FALSE);
+	return (ret);
+}
+
+/*
+ * om_get_default_locale
+ * This function returns the default locale as set by a call to
+ * set_default_locale.
+ * Input:	locale_info_t *loclistp - list to search
+ * Output:	none
+ * Return:	locale_info_t * pointer to structure for default locale.
+ * 		or NULL if none found.
+ * Error Handling:
+ *		OM_SUCCESS if locales found.
+ *		OM_FAILURE if no locales found.
+ */
+locale_info_t *
+om_get_def_locale(locale_info_t *loclistp)
+{
+	locale_info_t *lp;
+
+	for (lp = loclistp; lp != NULL; lp = lp->next)
+		if (lp->def_locale == B_TRUE)
+			return (lp);
+	return (NULL);
+}
+/*
+ * om_free_lang_names
+ * This function frees the memory associated with the char **list of lang
+ * names passed in.
+ * Input:	char **lang name list.
+ * Output:	none
+ * Return:	none
+ * Error Handling:
+ *		none
+ */
+void
+om_free_lang_names(char **listp)
+{
+	int	i = 0;
+
+	for (i = 0; listp[i] != NULL; i++) {
+		free(listp[i]);
+	}
+	free(listp);
+}
+
+/*
+ * om_free_lang_info
+ * This function frees the memory associated with the lang_info_t *list
+ * Input:	lang_info_t * langp - list to free
+ * Output:	none
+ * Return:	none
+ * Error Handling:
+ *		none
+ */
+void
+om_free_lang_info(lang_info_t *langp)
+{
+	lang_info_t *nextp;
+
+	while (langp != NULL) {
+		nextp = langp->next;
+		om_free_locale_info((locale_info_t *)langp->locale_info);
+		free(langp->lang);
+		free(langp->lang_name);
+		free(langp);
+		langp = nextp;
+	}
+}
+
+/*
+ * om_free_locale_info
+ * This function frees the memory associated with the locale_info_t *list
+ * Input:	locale_info_t * localep - list to free
+ * Output:	none
+ * Return:	none
+ * Error Handling:
+ *		none
+ */
+void
+om_free_locale_info(locale_info_t *localep)
+{
+	locale_info_t	*nextp;
+
+	while (localep != NULL) {
+		nextp = localep->next;
+		free(localep->locale_name);
+		free(localep->locale_desc);
+		free(localep);
+		localep = nextp;
+	}
+}
+/*
+ * Static functions.
+ */
+static int
+save_system_default_locale(char *locale)
+{
+
+	FILE 	*fp = (FILE *)NULL;
+
+	if (locale == NULL)
+		return (OM_FAILURE);
+
+	if ((fp = fopen(TMP_INITDEFSYSLOC, "w")) == (FILE *)NULL)
+		return (OM_FAILURE);
+	if (fprintf(fp, "%s\n", locale) < 0) {
+		(void) fclose(fp);
+		return (OM_FAILURE);
+	}
+	(void) fclose(fp);
+
+	if ((fp = fopen(TMP_DEFSYSLOC, "w")) == (FILE *)NULL)
+		return (OM_FAILURE);
+	if (fprintf(fp, "%s\n", locale) < 0) {
+		(void) fclose(fp);
+		return (OM_FAILURE);
+	}
+	(void) fclose(fp);
+	return (OM_SUCCESS);
+}
+
+
+static int
+lang_init(char *path, char **list, int *total, int *init_var)
+{
+	int	ret;
+
+	ret = build_language_list(path, list, total);
+	if (!ret) {
+		*init_var = 1;
+	}
+	return (ret);
+}
+
+/*
+ * Function
+ *		create_lang_entry
+ *
+ * Description
+ *		Create a language/locale list node and link
+ *		it into the lang/locale linked list.
+ *
+ * Scope
+ *		Private
+ *
+ * Parameters
+ *		lang - language to add
+ *		locale - locale which uses lang
+ *
+ * Return
+ *		none
+ *
+ */
+static int
+create_lang_entry(char *lang, char *locale, char *region,
+    lang_info_t **return_list, boolean_t is_default)
+{
+	lang_info_t	*tmp, *last, *new;
+	locale_info_t	*lp = NULL;
+	char		**trans_lang = NULL;
+	char		*tmp_lang = NULL;
+	char		*tmp_locale = NULL;
+	char		*code = NULL;
+	char		*desc = NULL;
+	int		total;
+	lang_info_t	*list = *return_list;
+
+	/*
+	 * For Chinese we have to handle it specially. There is Traditional
+	 * Chinese or Simplified Chinese. Everything else is a locale.
+	 */
+	if (strncmp(lang, "zh", 2) == 0) {
+		tmp_lang = strdup(dgettext(TEXT_DOMAIN,
+		    substitute_chinese_language(lang, &code)));
+		if (locale == NULL)
+			tmp_locale = strdup(dgettext(TEXT_DOMAIN, lang));
+	}
+	if (strncmp(lang, "C", 1) == 0) {
+		tmp_lang = strdup(dgettext(TEXT_DOMAIN,
+		    substitute_C_POSIX_language(&code)));
+		if (locale == NULL)
+			tmp_locale = strdup(dgettext(TEXT_DOMAIN, lang));
+	}
+
+	new = (lang_info_t *)malloc(sizeof (lang_info_t));
+	if (new == NULL)
+		goto error;
+
+	(void) memset(new, 0, sizeof (lang_info_t));
+
+	new->lang = (code != NULL) ? strdup(code): strdup(lang);
+	if (new->lang == NULL)
+		goto error;
+
+	if (tmp_lang != NULL) {
+		trans_lang = &tmp_lang;
+	} else
+		trans_lang = get_actual_languages(&new->lang, &total);
+
+	/*
+	 * We look for the lang name in our list of iso approved language
+	 * translations. If not found, then it isn't a language so
+	 * we don't create an entry for it.
+	 */
+
+	if (trans_lang != NULL && *trans_lang != NULL) {
+		new->lang_name  = *trans_lang;
+	} else {
+		om_set_error(OM_NOT_LANG);
+		goto error;
+	}
+
+	new->def_lang = is_default;
+
+	if (locale != NULL || tmp_locale != NULL) {
+		lp = (locale_info_t *)malloc(sizeof (locale_info_t));
+		if (lp == NULL) {
+			om_set_error(OM_NO_SPACE);
+			goto error;
+		}
+		(void) memset(lp, 0, sizeof (locale_info_t));
+
+		if (tmp_locale) {
+			lp->locale_name = tmp_locale;
+		} else {
+			lp->locale_name = strdup(locale);
+		}
+		if (lp->locale_name == NULL) {
+			om_set_error(OM_NO_SPACE);
+			goto error;
+		}
+
+		desc = get_locale_description(new->lang_name, region);
+		lp->locale_desc = desc;
+		new->locale_info = lp;
+		new->locale_info->def_locale = is_default;
+		new->n_locales++;
+	}
+	if (list != NULL) {
+		for (tmp = list, last = NULL; tmp != NULL;
+		    last = tmp, tmp = tmp->next) {
+			/* Everything is after English */
+			if (strcmp(tmp->lang, dgettext(TEXT_DOMAIN,
+			    "English")) == 0) {
+				break;
+			}
+			if (strcmp(tmp->lang, lang) > 0) {
+				break;
+			}
+		}
+		if (tmp == list) {
+			new->next = list;
+			list = new;
+		} else {
+			last->next = new;
+			new->next = tmp;
+		}
+	} else {
+		list = new;
+	}
+
+	free(code);
+	*(return_list) = list;
+	return (OM_SUCCESS);
+
+error:
+	free(code);
+	om_free_lang_info(new);
+	om_free_locale_info(lp);
+	return (OM_FAILURE);
+
+}
+/*
+ * Function
+ *		get_lang_entry
+ *
+ * Description
+ *		Get the language/locale list node which uses
+ *		the specified language
+ *
+ * Scope
+ *		Private
+ *
+ * Parameters
+ *		lang - language to search for
+ *
+ * Return
+ *		a pointer to the correct lang/locale node or NULL
+ *
+ */
+static lang_info_t *
+get_lang_entry(char *lang_name, lang_info_t *search_list)
+{
+	lang_info_t 	*list = NULL;
+	char		*tmp_lang = NULL;
+	char		*code = NULL;
+	boolean_t	found = B_FALSE;
+
+	if (lang_name == NULL)
+		return (NULL);
+	/*
+	 * Chinese language names are stored differently.
+	 */
+
+	if (strncmp(lang_name, "zh", 2) == 0)  {
+		tmp_lang = strdup(dgettext(TEXT_DOMAIN,
+		    substitute_chinese_language(lang_name, &code)));
+	}
+
+	for (list = search_list; list != NULL; list = list->next) {
+		if (code) {
+			if (strcmp(list->lang, code) == 0) {
+				found = B_TRUE;
+				break;
+			}
+		} else {
+			if (strcmp(list->lang, lang_name) == 0) {
+				found = B_TRUE;
+				break;
+			}
+		}
+	}
+
+	free(tmp_lang);
+	free(code);
+
+	if (!found)
+		return (NULL);
+	return (list);
+}
+
+/*
+ * Function
+ *		add_locale_entry_to_lang
+ *
+ * Description
+ *		Add an additional locale to the list of
+ *		locales that use lang.
+ *
+ * Scope
+ *		Private
+ *
+ * Parameters
+ *		langp - pointer to the lang/locale node to add locale to
+ *		locale - locale which uses lang
+ *
+ * Return
+ *		none
+ *
+ */
+static void
+add_locale_entry_to_lang(lang_info_t *langp, char *locale_name, char *region,
+    boolean_t is_default)
+{
+	char		*desc = NULL;
+	locale_info_t 	*locp = NULL;
+	locale_info_t	*tmp = NULL;
+
+	tmp = langp->locale_info;
+
+	/*
+	 * Check for previous inclusion of this locale entry.
+	 */
+
+	while (tmp != NULL) {
+		if (strcmp(tmp->locale_name, locale_name) == 0)
+			return;
+		tmp = tmp->next;
+	}
+	/*
+	 * Allocate space for new entry.
+	 */
+	locp = (locale_info_t *)malloc(sizeof (locale_info_t));
+	if (locp == NULL) {
+		om_set_error(OM_NO_SPACE);
+		return;
+	}
+	(void) memset(locp, 0, sizeof (locale_info_t));
+
+	locp->locale_name = strdup(locale_name);
+	if (locp->locale_name == NULL) {
+		om_set_error(OM_NO_SPACE);
+		om_free_locale_info(locp);
+		return;
+	}
+
+	desc = get_locale_description(langp->lang_name, region);
+	locp->locale_desc = desc;
+	locp->def_locale = is_default;
+
+	locp->next = langp->locale_info;
+	langp->locale_info = locp;
+	langp->def_lang = is_default;
+	langp->n_locales++;
+}
+
+static void
+build_install_ll_list(char *nlspath, char **install_list, int lang_total,
+    lang_info_t **return_list, int *ll_total)
+{
+	char 		path[MAXPATHLEN];
+	char		trans[MAX_LOCALE + 1];
+	char 		linebuf[128];
+	FILE		*fp;
+	char		*c, *c2;
+	int		i, ret = 0;
+	lang_info_t	*lp;
+	locale_info_t	*locp = NULL;
+	int		num_entries = 0;
+	boolean_t	is_default = B_FALSE;
+	char		*start = NULL, *t = NULL, *lang = NULL;
+	char		*region = NULL, *encoding = NULL;
+
+	*return_list = NULL;
+
+	if (install_list == NULL || *install_list == NULL) {
+		om_set_error(OM_INVALID_LANG_LIST);
+		return;
+	}
+
+	(void) memset(trans, 0, sizeof (trans));
+	/*
+	 * For the installer application supported languages we only
+	 * want the non-UTF-8 codeset. Why? Because there is not full
+	 * locale support for UTF-8 in the miniroot.
+	 */
+	for (i = 0; i < lang_total && install_list[i] != NULL; i++) {
+		start = install_list[i];
+		lang = get_locale_component(&t, &start);
+
+		if (start && *t == COUNTRY_SEP)
+			region = get_locale_component(&t, &start);
+
+		if (start && *t == CODESET_SEP)
+			encoding = get_locale_component(&t, &start);
+
+		if (encoding != NULL) {
+			if (strcmp(encoding, UTF) == 0) {
+				free(encoding);
+				free(region);
+				free(lang);
+				encoding = NULL;
+				region = NULL;
+				lang = NULL;
+				continue;
+			}
+		}
+
+		if (strncmp(lang, "zh", 2) == 0) {
+			if (region != NULL && strcmp(region, "TW") == 0) {
+				om_errno = handle_chinese_language(region,
+				    &lang);
+				if (om_errno != OM_SUCCESS) {
+					goto error;
+				}
+			}
+		} else if (strcmp(lang, "C") == 0 ||
+		    strcmp(lang, "POSIX") == 0) {
+			free(lang);
+			lang = strdup("en");
+			is_default = B_TRUE;
+			if (lang == NULL) {
+				om_set_error(OM_NO_SPACE);
+				goto error;
+			}
+		}
+
+		if ((lp = get_lang_entry(lang, *return_list)) != NULL) {
+			continue;
+		} else {
+			ret = create_lang_entry(install_list[i],
+			    install_list[i], region, return_list, is_default);
+			if (!ret)
+				num_entries++;
+		}
+		free(lang);
+		free(region);
+		free(encoding);
+		lang = NULL;
+		region = NULL;
+		encoding = NULL;
+		is_default = B_FALSE;
+	}
+
+	/*
+	 * For the install application language specifications, which
+	 * are located at /usr/lib/install/data/lib/locale, we need
+	 * to ensure we have an up to date locale/lang name for
+	 * translation. The lcttab file in /usr/lib/locale provides
+	 * the correct mapping.
+	 */
+	lp = *return_list;
+	(void) snprintf(path, sizeof (path), "%s/lcttab", nlspath);
+	if ((fp = fopen(path, "r"))) {
+		for (i = 0; lp != NULL; lp = lp->next) {
+			locp = lp->locale_info;
+			rewind(fp);
+			while (fgets(linebuf, 128, fp)) {
+				if (strlen(linebuf) == 0 ||
+				    linebuf[0] == '#') {
+					continue;
+				}
+				/* Search for whitespace */
+				for (c = linebuf; *c && !isspace(*c); c++)
+					;
+
+				if (*c != '\0') {
+					/* End of line - invalid input */
+					continue;
+				}
+				*c = '\0';
+				if (strcmp(linebuf, locp->locale_name) != 0) {
+					continue;
+				}
+
+				/* Found the old name - get the new version */
+				for (c++; *c && isspace(*c); c++)
+					;
+
+				if (!*c) {
+					continue;
+				}
+				for (c2 = c; *c2 && !isspace(*c2); c2++)
+					;
+
+				*c2 = '\0';
+				/*
+				 * We are not interested in UTF-8
+				 * codesets for now.
+				 */
+				if (strstr(c, UTF) != NULL)
+					continue;
+				(void) strcpy(trans, c);
+				break;
+			} /* end while */
+
+			/*
+			 * We may have not found a match in the lcttab file.
+			 * If not, copy the original locale data in and
+			 * search for locale_map.
+			 */
+			if (trans[0] == '\0')
+				(void) strcpy(trans, locp->locale_name);
+
+			locp = (locale_info_t *)malloc(sizeof (locale_info_t));
+			if (locp == NULL) {
+				om_set_error(OM_NO_SPACE);
+				goto error;
+			}
+			(void) memset(locp, 0, sizeof (locale_info_t));
+			locp->locale_name = strdup(trans);
+			if (locp->locale_name == NULL) {
+				om_free_locale_info(locp);
+				om_set_error(OM_NO_SPACE);
+				goto error;
+			}
+			(void) memset(trans, 0, sizeof (trans));
+			/*
+			 * free the original lang_info_t->locale_info
+			 * data.
+			 */
+			om_free_locale_info(lp->locale_info);
+			lp->locale_info = locp;
+			lp->n_locales++;
+		} /* end for */
+	} /* end if */
+
+	/*
+	 * Now, translate language names, in this order, in to native
+	 * locale based on current locale data associated with this language.
+	 */
+	translate_lang_names(return_list);
+	*ll_total = num_entries;
+	(void) fclose(fp);
+	return;
+
+error:
+	(void) fclose(fp);
+	om_free_lang_info(*return_list);
+	*return_list = NULL;
+	*ll_total = 0;
+	free(lang);
+	free(region);
+	free(encoding);
+	free(start);
+}
+
+static void
+build_ll_list(char **list, int lang_total, lang_info_t **return_list,
+    int *total)
+{
+	int		i;
+	int		ret = 0;
+	int		num_langs = 0;
+	char		*lang = NULL, *encoding = NULL, *region = NULL;
+	char		*locale = NULL;
+	lang_info_t	*lp = NULL;
+	char		*orig, *start = NULL;
+	char		*t = NULL;
+	boolean_t	is_default = B_FALSE;
+
+	*total = 0;
+
+	/*
+	 * lang_list passed in is a sorted list of the data found in
+	 * the locale directory. Take this sorted list,
+	 * parse appropriately, and insert locale data for each language
+	 * in to the return_list.
+	 */
+
+	for (i = 0; i < lang_total; i++)  {
+		orig = start = list[i];
+		if (!is_valid_locale(list[i]))
+			continue;
+
+		t = NULL;
+		lang = get_locale_component(&t, &start);
+
+		/*
+		 * If we found a country or codeset sep then we found a
+		 * locale.  The lang value is the language part of
+		 * the lang/locale pair. What was in the original list
+		 * is the locale.
+		 */
+		if (start && *t == COUNTRY_SEP) {
+			region = get_locale_component(&t, &start);
+		}
+
+		if (start && *t == CODESET_SEP) {
+			encoding = get_locale_component(&t, &start);
+		}
+
+		if (strncmp(lang, "zh", 2) == 0) {
+			/*
+			 * If there is a region with the Chinese lang,
+			 * we need to ensure that it is not its own language.
+			 * Chinese specifications for language include
+			 * the region, such as zh_HK and zh_TW.
+			 * If no region is found, then lang is simply lang
+			 * from above.
+			 */
+			if (region) {
+				om_errno = handle_chinese_language(region,
+				    &lang);
+				if (om_errno != OM_SUCCESS) {
+					goto error;
+				}
+			} else {
+				/*
+				 * We need to account for Simplified Chinese,
+				 * EUC in the locale list. Its locale name
+				 * does not include a region.
+				 */
+				locale = strdup(lang);
+				if (locale == NULL) {
+					om_set_error(OM_NO_SPACE);
+					goto error;
+				}
+			}
+		} else if (strcmp(lang, "C") == 0 ||
+		    strcmp(lang, "POSIX") == 0) {
+			free(lang);
+			lang = strdup("en");
+			if (lang == NULL) {
+				om_set_error(OM_NO_SPACE);
+				goto error;
+			}
+			locale = strdup(lang);
+			if (locale == NULL) {
+				om_set_error(OM_NO_SPACE);
+				goto error;
+			}
+		}
+		/*
+		 * Locale is a combination of  lang, country and codeset.
+		 */
+		if (encoding != NULL) {
+			locale = strdup(orig);
+			if (locale == NULL) {
+				om_set_error(OM_NO_SPACE);
+				goto error;
+			}
+		}
+
+		/*
+		 * If we don't have the locale value, we haven't found
+		 * anything we are interested in, so skip.
+		 */
+		if (locale != NULL)  {
+			is_default = is_locale_in_installer_lang(locale);
+			if ((lp = get_lang_entry(lang, *return_list)) != NULL) {
+				add_locale_entry_to_lang(lp, locale, region,
+				    is_default);
+			} else {
+				ret = create_lang_entry(lang, locale, region,
+				    return_list, is_default);
+				if (!ret) {
+					num_langs++;
+					om_debug_print(OM_DBGLVL_INFO,
+					    "num_langs = %d\n", num_langs);
+				}
+			}
+		}
+		free(region);
+		free(encoding);
+		free(lang);
+		free(locale);
+		region = NULL;
+		encoding = NULL;
+		lang = NULL;
+		locale = NULL;
+	}
+	*total = num_langs;
+	return;
+error:
+	om_free_lang_info(*return_list);
+	*return_list = NULL;
+	free(region);
+	free(encoding);
+	free(lang);
+	free(locale);
+}
+
+/*
+ * build_language_list:
+ *
+ *	The idea is to scan the directories under "path" and
+ *	build the language list, char ** list, associated with
+ *	the "path".
+ */
+static int
+build_language_list(char *path, char **list, int *total)
+{
+	DIR		*locale_dir;
+	struct dirent	*locale;		/* entries in locale_dir */
+	int		i = 0;
+
+	/*
+	 * Read in language data from the locale directory.
+	 */
+	(void) memset(list, 0, sizeof (*list));
+	locale_dir = opendir(path);
+	if (locale_dir == NULL) {
+		if (errno == EACCES) {
+			om_set_error(OM_PERMS);
+		} else if (errno != EMFILE && errno != ENFILE) {
+			om_set_error(OM_NO_LOCALE_DIR);
+		} else {
+			om_set_error(OM_TOO_MANY_FD);
+		}
+		goto error;
+	}
+
+	while (locale = readdir(locale_dir)) {
+		/*
+		 * Exclude current and parent directory. Make sure
+		 * we are not over our buffer limit.
+		 */
+		if (strcmp(locale->d_name, ".") == 0 ||
+		    strcmp(locale->d_name, "..") == 0)
+			continue;
+
+		list[i] = strdup(locale->d_name);
+		if (list[i] == NULL) {
+			om_set_error(OM_NO_SPACE);
+			goto error;
+		}
+		i++;
+	}
+	*total = i;
+	(void) closedir(locale_dir);
+	return (OM_SUCCESS);
+
+error:
+	om_free_lang_names(list);
+	*list = NULL;
+	(void) closedir(locale_dir);
+	return (OM_FAILURE);
+}
+/*
+ * This function reads a locales locale_map file to get the settings
+ * that should be used for localization.
+ *
+ * Input: File *fp  File pointer to locale_map file
+ * Output: Each type of locale category value is returned.
+ */
+int
+read_locale_file(FILE *fp, char *lang, char *lc_collate, char *lc_ctype,
+	char *lc_messages, char *lc_monetary, char *lc_numeric, char *lc_time)
+{
+	int status = 0;
+	char line[BUFSIZ];
+
+	(void) strcpy(lc_collate, "C");
+	(void) strcpy(lc_ctype, "C");
+	(void) strcpy(lc_messages, "C");
+	(void) strcpy(lc_monetary, "C");
+	(void) strcpy(lc_numeric, "C");
+	(void) strcpy(lc_time, "C");
+
+	while (fgets(line, BUFSIZ, fp) != NULL) {
+		/*
+		 * Remove the trailing newline, and strip any comments that
+		 * may appear on the read line.
+		 */
+		line[strlen(line) - 1] = '\0';
+		(void) strip_comment(line);
+
+		if (strncmp(STR_LANG, line, LEN_LANG) == 0) {
+			(void) strcpy(lang, line + LEN_LANG);
+			status = 1;
+		} else if (strncmp(STR_LC_COLLATE, line, LEN_LC_COLLATE) == 0) {
+			(void) strcpy(lc_collate, line + LEN_LC_COLLATE);
+			status = 2;
+		} else if (strncmp(STR_LC_CTYPE, line, LEN_LC_CTYPE) == 0) {
+			(void) strcpy(lc_ctype, line + LEN_LC_CTYPE);
+			status = 2;
+		} else if (strncmp(STR_LC_MESSAGES, line, LEN_LC_MESSAGES)
+		    == 0) {
+			(void) strcpy(lc_messages, line + LEN_LC_MESSAGES);
+			status = 2;
+		} else if (strncmp(STR_LC_MONETARY, line, LEN_LC_MONETARY)
+		    == 0) {
+			(void) strcpy(lc_monetary, line + LEN_LC_MONETARY);
+			status = 2;
+		} else if (strncmp(STR_LC_NUMERIC, line, LEN_LC_NUMERIC) == 0) {
+			(void) strcpy(lc_numeric, line + LEN_LC_NUMERIC);
+			status = 2;
+		} else if (strncmp(STR_LC_TIME, line, LEN_LC_TIME) == 0) {
+			(void) strcpy(lc_time, line + LEN_LC_TIME);
+			status = 2;
+		}
+	}
+
+	if (status == 1) {
+		/*
+		 * There's a LANG, but nothing else, so populate all of the
+		 * fields with the value put in LANG
+		 */
+		(void) strcpy(lc_collate, lang);
+		(void) strcpy(lc_ctype, lang);
+		(void) strcpy(lc_messages, lang);
+		(void) strcpy(lc_monetary, lang);
+		(void) strcpy(lc_numeric, lang);
+		(void) strcpy(lc_time, lang);
+	}
+	return (status);
+}
+
+/*
+ * Function:	strip_comment
+ * Description:	Given a string of the form 'foo # comment', strip the comment
+ *		text, the comment marker, and the whitespace (if any) preceding
+ *		it.  This modification is done in-place on the passed in string.
+ * Scope:	private
+ * Arguments:	buf	- [RO, *RW] (char *)
+ *			  The buffer from which the comment is to be stripped.
+ * Returns:	char *	- buf, the pointer passed in
+ */
+static char *
+strip_comment(char *buf)
+{
+	char *comchr;
+
+	if (buf == NULL || (comchr = strchr(buf, '#')) == NULL)
+		return (buf);
+
+	for (; comchr != buf && isspace(*(comchr - 1)); comchr--)
+		;
+	*comchr = '\0';
+	return (buf);
+}
+static void
+set_lang(char *locale)
+{
+	static char	tmpstr[MAX_LOCALE + 6];
+
+	(void) setlocale(LC_ALL, locale);
+	(void) snprintf(tmpstr, sizeof (tmpstr), "LANG=%s", locale);
+	(void) putenv(tmpstr);
+}
+
+static void
+set_lc(char *lc_collate, char *lc_ctype, char *lc_messages, char *lc_monetary,
+	char *lc_numeric, char *lc_time)
+{
+
+	/*
+	 * The longest addition to a locale value is LC_MESSAGES=. This
+	 * plus the NULL value is 14 chars. Added 15 for padding as
+	 * a result.
+	 */
+	static char	tmpstr1[MAX_LOCALE + 15];
+	static char	tmpstr2[MAX_LOCALE + 15];
+	static char	tmpstr3[MAX_LOCALE + 15];
+	static char	tmpstr4[MAX_LOCALE + 15];
+	static char	tmpstr5[MAX_LOCALE + 15];
+	static char	tmpstr6[MAX_LOCALE + 15];
+	char		*loc = NULL;
+
+	loc = setlocale(LC_COLLATE, lc_collate);
+	if (loc)
+		om_debug_print(OM_DBGLVL_INFO, "lc_collate set to %s\n", loc);
+	else
+		om_debug_print(OM_DBGLVL_WARN,
+		    "Could not set lc_collate value\n");
+
+	(void) snprintf(tmpstr1, sizeof (tmpstr1), "LC_COLLATE=%s", lc_collate);
+	(void) putenv(tmpstr1);
+
+	loc = setlocale(LC_CTYPE, lc_ctype);
+	if (loc)
+		om_debug_print(OM_DBGLVL_INFO,
+		    "lc_ctype set to %s\n", loc);
+	else
+		om_debug_print(OM_DBGLVL_WARN,
+		    "Could not set lc_ctype value\n");
+	(void) snprintf(tmpstr2, sizeof (tmpstr2), "LC_CTYPE=%s", lc_ctype);
+	(void) putenv(tmpstr2);
+
+	loc = setlocale(LC_MESSAGES, lc_messages);
+	if (loc)
+		om_debug_print(OM_DBGLVL_INFO,
+		    "lc_messages set to %s\n", loc);
+	else
+		om_debug_print(OM_DBGLVL_WARN,
+		    "Could not set lc_messages value\n");
+	(void) snprintf(tmpstr3, sizeof (tmpstr3),
+	    "LC_MESSAGES=%s", lc_messages);
+	(void) putenv(tmpstr3);
+
+	loc =  setlocale(LC_MONETARY, lc_monetary);
+	if (loc)
+		om_debug_print(OM_DBGLVL_INFO,
+		    "lc_monetary set to %s\n", loc);
+	else
+		om_debug_print(OM_DBGLVL_WARN,
+		    "Could not set lc_monetary value\n");
+	(void) snprintf(tmpstr4, sizeof (tmpstr4),
+	    "LC_MONETARY=%s", lc_monetary);
+	(void) putenv(tmpstr4);
+
+	loc = setlocale(LC_NUMERIC, lc_numeric);
+	if (loc)
+		om_debug_print(OM_DBGLVL_INFO,
+		    "lc_numeric set to %s\n", loc);
+	else
+		om_debug_print(OM_DBGLVL_WARN,
+		    "Could not set lc_numeric value\n");
+	(void) snprintf(tmpstr5, sizeof (tmpstr5), "LC_NUMERIC=%s", lc_numeric);
+	(void) putenv(tmpstr5);
+
+	loc = setlocale(LC_TIME, lc_time);
+	if (loc)
+		om_debug_print(OM_DBGLVL_INFO,
+		    "lc_time set to %s\n", loc);
+	else
+		om_debug_print(OM_DBGLVL_WARN,
+		    "Could not set lc_time value\n");
+	(void) snprintf(tmpstr6, sizeof (tmpstr6), "LC_TIME=%s", lc_time);
+	(void) putenv(tmpstr6);
+}
+
+/*
+ * Name:	get_locale_description
+ * Description:	Read the locale_description file for a given locale, returning
+ *		a pointer to a buffer containing the contents of that
+ *		file.
+ * Scope:	private
+ * Arguments:	locale	- [RO, *RO] (char *)
+ *			  The locale whose locale_description is to be read
+ * Returns:	char *	- The first line of the locale_description
+ */
+static char *
+get_locale_description(char *lang, char *region)
+{
+	char	*user_desc;
+
+	/*
+	 * Chinese and Korean are handled differently. This is due to
+	 * the locale description for each of these being different than
+	 * the standard.
+	 */
+
+	if (region == NULL) {
+		if (strcmp(lang, dgettext(TEXT_DOMAIN, TRADITIONAL_CHINESE))
+		    == 0 ||
+		    strcmp(lang, dgettext(TEXT_DOMAIN, SIMPLIFIED_CHINESE))
+		    == 0) {
+			region = strdup("zh");
+		} else if (strcmp(lang, dgettext(TEXT_DOMAIN, "Korean")) == 0) {
+			region = strdup("ko");
+		}
+	}
+	user_desc = translate_description(lang, region);
+	return (user_desc);
+}
+
+static char *
+translate_description(char *lang, char *region)
+{
+	char 		*trans_desc = NULL;
+	int		len = 0, i;
+	int		szcountry = sizeof (orchestrator_country_list) /
+	    sizeof (orchestrator_country_list[0]);
+
+	char		*tmp_ctrystring = NULL;
+
+	if (lang == NULL || region == NULL) {
+		return (NULL);
+	}
+
+	for (i = 0; i < szcountry; i++) {
+		/*
+		 * Translate the country code for this locale.
+		 */
+		if (strncasecmp(region,
+		    orchestrator_country_list[i].country_code, 2) == 0) {
+			tmp_ctrystring =
+			    dgettext(TEXT_DOMAIN,
+			    orchestrator_country_list[i].country_name);
+			break;
+		}
+	}
+	if (tmp_ctrystring) {
+		len = strlen(lang) + strlen(tmp_ctrystring) + 4;
+		trans_desc = (char *)malloc(len);
+		if (trans_desc == NULL)
+			return (NULL);
+		(void) snprintf(trans_desc, len, "%s (%s)",
+		    lang, tmp_ctrystring);
+	}
+	return (trans_desc);
+}
+
+static char **
+get_actual_languages(char **list, int *total)
+{
+	char	**lp;
+	char	**lang_listp = NULL;
+	size_t	sz;
+	int	ret = 0;
+	int	i, j, k = 0;
+
+
+	*total = 0;
+
+	if (list == NULL || *list == NULL)
+		return (NULL);
+
+	sz = sizeof (orchestrator_lang_list)/sizeof (orchestrator_lang_list[0]);
+
+	lp = list;
+	for (i = 0; lp[i] != '\0'; i++) {
+		for (j = 0; j < sz; j++) {
+			if (strncmp(lp[i],
+			    (char *)orchestrator_lang_list[j].lang_code,
+			    2) == 0) {
+				ret = add_lang_to_list(&lang_listp,
+				    lp[i], &k, j);
+				if (ret) {
+					om_free_lang_names(lang_listp);
+					return (NULL);
+				}
+				break;
+			}
+		}
+	}
+	/*
+	 * No lang translation found. Return existing list.
+	 */
+	if (j == sz) {
+		return (lang_listp);
+	}
+	*total = k;
+	om_set_error(OM_SUCCESS);
+	return (lang_listp);
+}
+
+static int
+add_lang_to_list(char ***list, char *locale, int *k, int j)
+{
+	int	i;
+	char	**lpp = *list;
+	char	**tmp_list;
+	char	*tmp = NULL;
+	char	*code;
+
+	tmp_list = *list;
+
+	if (strncmp(locale, "zh", 2) == 0) {
+		tmp = strdup(dgettext(TEXT_DOMAIN,
+		    substitute_chinese_language(locale, &code)));
+	} else if (strcmp(locale, "C") == 0 || strcmp(locale, "POSIX") == 0 ||
+	    strcmp(locale, "C/POSIX") == 0) {
+		tmp = strdup(dgettext(TEXT_DOMAIN, "English"));
+	} else {
+		tmp = strdup(dgettext(TEXT_DOMAIN,
+		    orchestrator_lang_list[j].lang_name));
+	}
+
+	if (tmp == NULL) {
+		om_set_error(OM_NO_SPACE);
+		return (OM_FAILURE);
+	}
+
+	if (tmp_list != NULL) {
+		/*
+		 * Search for existence of this language in the list already
+		 */
+		for (i = 0; tmp_list[i] != NULL && tmp_list[i] != '\0' &&
+		    i < *k; i++) {
+			if (strcmp(tmp, tmp_list[i]) == 0) {
+				free(tmp);
+				return (OM_SUCCESS);
+			}
+		}
+	}
+	tmp_list = (char **)realloc(*list, ((*k) + 1) * sizeof (char *));
+	if (tmp_list == NULL) {
+		free(tmp);
+		om_set_error(OM_NO_SPACE);
+		return (OM_FAILURE);
+	}
+	lpp = *list = tmp_list;
+	lpp[*k] = tmp;
+	(*k)++;
+	return (OM_SUCCESS);
+}
+
+static boolean_t
+is_valid_locale(char *locale)
+{
+
+	char	path[MAXPATHLEN];
+	struct	stat stat_buf;
+
+	if (locale == NULL)
+		return (B_FALSE);
+
+	if (strstr(locale, UTF) == NULL)
+		return (B_FALSE);
+
+	(void) snprintf(path, sizeof (path), "%s/%s/locale_map",
+	    NLS_PATH, locale);
+	if ((stat(path, &stat_buf) == 0) &&
+	    ((stat_buf.st_mode & S_IFMT) == S_IFREG)) {
+		return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+static char *
+substitute_C_POSIX_language(char **code)
+{
+	char	*lang = NULL;
+
+	/*
+	 * locale is C and or POSIX. Set to English, set code
+	 * to 'en'.
+	 */
+	lang = strdup(dgettext(TEXT_DOMAIN, "English"));
+	if (lang == NULL) {
+		om_set_error(OM_NO_SPACE);
+		*code = NULL;
+		return (lang);
+	}
+	*code = strdup("en");
+	if (*code == NULL) {
+		free(lang);
+		lang = NULL;
+		om_set_error(OM_NO_SPACE);
+	}
+	return (lang);
+}
+
+static char *
+substitute_chinese_language(char *locale, char **code)
+{
+	int 		i;
+	char		*sub = NULL;
+
+	*code = NULL;
+
+	for (i = 0; chinese_values[i].lang; i++) {
+		if (strcmp(locale, chinese_values[i].lang) == 0) {
+			sub = strdup(chinese_values[i].lang_name);
+			*code = strdup(chinese_values[i].lang_code);
+			if (sub == NULL || *code == NULL) {
+				free(sub);
+				om_set_error(OM_NO_SPACE);
+				return (NULL);
+			} else {
+				return (sub);
+			}
+		}
+	}
+	return (sub);
+}
+
+static void
+sort_lang_list(char **unsorted_list, int total)
+{
+	qsort((char **)unsorted_list, total, sizeof (char *), list_cmp);
+}
+
+static int
+handle_chinese_language(char *region, char **lang)
+{
+	int	len;
+	char	*chinese_lang;
+
+
+	len = strlen(*lang) + strlen(region) + 3;
+	chinese_lang = (char *)malloc(len);
+	/*
+	 * If we cannot allocate new language data, return error, but
+	 * don't modify language value. Allow caller to determine what
+	 * to do.
+	 */
+	if (chinese_lang == NULL) {
+		om_set_error(OM_NO_SPACE);
+		return (OM_FAILURE);
+	}
+	(void) snprintf(chinese_lang, len, "%s%s%s", *lang, "_", region);
+	free(*lang);
+	*lang = chinese_lang;
+	return (OM_SUCCESS);
+}
+static int
+list_cmp(const void *p1, const void *p2)
+{
+	return (strcmp(*(char **)p1, *(char **)p2));
+
+}
+/*
+ * This function gets each of the locale components. It does so
+ * by looking for each component of a locale, as noted above as defines.
+ * It returns each segment in t, or NULL if no additional component is found.
+ */
+static char *
+get_locale_component(char **t, char **start)
+{
+	char	*result = NULL;
+
+	end_of_comp(t, start);
+	result = copy_up_to(*start, *t);
+	*start = (**t != '\0') ? *t + 1: NULL;
+	return (result);
+}
+
+/*
+ * This function looks for each component of the locale string passed in
+ * in the 'start' parameter. If it finds one it returns a pointer to that
+ * component. Or NULL if not found.
+ */
+static void
+end_of_comp(char **t, char **start)
+{
+	(((*t) = strchr((*start), COUNTRY_SEP)) != NULL) ||
+	    (((*t) = strchr((*start), CODESET_SEP)) != NULL) ||
+	/*LINTED*/
+	    (((*t) = strchr((*start), '\0')) != NULL);
+}
+
+static char *
+copy_up_to(char *start, char *end)
+{
+	/*
+	 * XXX look at this.
+	 */
+
+	ptrdiff_t	diff;
+	char		*sub = NULL;
+
+	if (end == NULL) {
+		diff = strlen(start);
+	} else {
+		/*LINTED*/
+		diff = end - start;
+	}
+	sub = (char *)malloc(diff + 1);
+	(void) memset(sub, 0, diff + 1);
+	(void) strlcpy(sub, start, (diff + 1));
+	return (sub);
+}
+
+static boolean_t
+is_locale_in_installer_lang(char *locale_name)
+{
+	if (app_locale == NULL) {
+		app_locale = strdup(setlocale(LC_MESSAGES, NULL));
+	}
+
+	if (app_locale != NULL) {
+		if (strcmp(locale_name, app_locale) == 0) {
+			/* locale name is same */
+			return (B_TRUE);
+		} else if (strncmp(locale_name, app_locale, 2) == 0) {
+			/* language part is same */
+			if ((strncmp(locale_name, "zh_TW", 5) == 0) ||
+			    (strncmp(locale_name, "zh_HK", 5) == 0)) {
+				/* traditional Chinese */
+				if ((strncmp(app_locale, "zh_TW", 5) == 0) ||
+				    (strncmp(app_locale, "zh_HK", 5) == 0)) {
+					return (B_TRUE);
+				}
+			} else if (strncmp(locale_name, "zh", 2) == 0) {
+				/* simplified Chinese */
+				if ((strncmp(app_locale, "zh_TW", 5) != 0) &&
+				    (strncmp(app_locale, "zh_HK", 5) != 0)) {
+					return (B_TRUE);
+				}
+			} else {
+				/* others */
+				return (B_TRUE);
+			}
+		} else if (strncmp(locale_name, "en", 2) == 0) {
+			/* English */
+			if (strcmp(app_locale, "C") == 0) {
+				return (B_TRUE);
+			}
+		}
+	}
+
+	return (B_FALSE);
+}
+void
+om_save_locale(char *locale, boolean_t install_only)
+{
+	FILE 	*fp, *tfp;
+	char	line[BUFSIZ];
+	char	tfile[80];
+	char	target[MAXPATHLEN];
+
+	/*
+	 * If this is only setting the installation app locale we don't
+	 * want to modify the users /etc/default/init file just yet. That
+	 * will happen later.
+	 */
+
+	if (install_only) {
+		update_env(locale);
+	}
+
+	(void) sprintf(tfile, "/tmp/orchlocale%ld", getpid());
+	(void) snprintf(target, sizeof (target), "%s%s", get_rootdir(),
+	    INIT_FILE);
+
+	if ((tfp = fopen(tfile, "w")) == NULL)
+		return;
+
+	if ((fp = fopen(target, "r")) != NULL) {
+		while (fgets(line, BUFSIZ, fp) != NULL) {
+			if (strncmp("LANG=", line, 5) == 0)
+				continue;
+			if (strncmp("LC_", line, 3) == 0)
+				continue;
+			if (fputs(line, tfp) == EOF) {
+				(void) fclose(fp);
+				(void) fclose(tfp);
+				return;
+			}
+		}
+	}
+	(void) fclose(fp);
+	update_init(tfp, locale);
+	(void) fclose(tfp);
+
+	if ((fp = fopen(target, "w")) == NULL)
+		return;
+
+	if ((tfp = fopen(tfile, "r")) == NULL) {
+		(void) fclose(fp);
+		return;
+	}
+
+	while (fgets(line, BUFSIZ, tfp) != NULL)
+		if (fputs(line, fp) == EOF)
+			break;
+	(void) fclose(fp);
+	(void) fclose(tfp);
+}
+
+static void
+update_env(char *locale)
+{
+	char path[MAXPATHLEN];
+	char lc_collate[MAX_LOCALE];
+	char lc_ctype[MAX_LOCALE];
+	char lc_messages[MAX_LOCALE];
+	char lc_monetary[MAX_LOCALE];
+	char lc_numeric[MAX_LOCALE];
+	char lc_time[MAX_LOCALE];
+	char lang[MAX_LOCALE];
+	FILE *mfp;
+	int rc;
+
+	(void) snprintf(path, sizeof (path), "%s/%s/locale_map",
+	    NLS_PATH, locale);
+	if ((mfp = fopen(path, "r")) == NULL) {
+		set_lang(locale);
+	} else {
+		rc = read_locale_file(mfp, lang, lc_collate, lc_ctype,
+		    lc_messages, lc_monetary, lc_numeric, lc_time);
+		(void) fclose(mfp);
+
+		if (rc == 1) {
+			set_lang(lc_messages);
+		} else {
+			set_lc(lc_collate, lc_ctype, lc_messages, lc_monetary,
+			    lc_numeric, lc_time);
+		}
+	}
+}
+
+static void
+update_init(FILE *fp, char *locale)
+{
+	char path[MAXPATHLEN];
+	char lc_collate[MAX_LOCALE];
+	char lc_ctype[MAX_LOCALE];
+	char lc_messages[MAX_LOCALE];
+	char lc_monetary[MAX_LOCALE];
+	char lc_numeric[MAX_LOCALE];
+	char lc_time[MAX_LOCALE];
+	char lang[MAX_LOCALE];
+	FILE *mfp;
+	int rc;
+
+	(void) snprintf(path, sizeof (path), "%s/%s/locale_map",
+	    NLS_PATH, locale);
+	if ((mfp = fopen(path, "r")) == NULL) {
+		if (strcmp(locale, "C") != 0) {
+			(void) fprintf(fp, "LANG=%s\n", locale);
+		}
+		set_lang(locale);
+	} else {
+		rc = read_locale_file(mfp, lang, lc_collate, lc_ctype,
+		    lc_messages, lc_monetary, lc_numeric, lc_time);
+		(void) fclose(mfp);
+
+		if (rc == 1) {
+			(void) fprintf(fp, "LANG=%s\n", lc_messages);
+			set_lang(lc_messages);
+		} else {
+			(void) fprintf(fp, "LC_COLLATE=%s\n", lc_collate);
+			(void) fprintf(fp, "LC_CTYPE=%s\n", lc_ctype);
+			(void) fprintf(fp, "LC_MESSAGES=%s\n", lc_messages);
+			(void) fprintf(fp, "LC_MONETARY=%s\n", lc_monetary);
+			(void) fprintf(fp, "LC_NUMERIC=%s\n", lc_numeric);
+			(void) fprintf(fp, "LC_TIME=%s\n", lc_time);
+		}
+
+	}
+}
+
+static void
+translate_lang_names(lang_info_t **list)
+{
+
+	char		trans[512];
+	lang_info_t	*langp;
+
+	/*
+	 * Set locale to en_US.UTF-8. It doesn't really matter
+	 * what it is set to expect it cannot be C. dgettext does
+	 * not pick up the translated strings if the current locale
+	 * is C.
+	 */
+	set_lang("en_US.UTF-8");
+	for (langp = *list; langp != NULL; langp = langp->next) {
+		if (langp->lang_name != NULL) {
+			(void) strcpy(trans,
+			    dgettext("SUNW_INSTALL_LANG",
+			    langp->lang_name));
+			/*
+			 * Free original lang name.
+			 */
+			free(langp->lang_name);
+			langp->lang_name = strdup(trans);
+			if (langp->lang_name == NULL) {
+				/*
+				 * Log a message. leave the other
+				 * lang names untranslated.
+				 * Otherwise, would have to free
+				 * full list and provide
+				 * the user with nothing.
+				 */
+				om_debug_print(OM_DBGLVL_ERR,
+				    "Couldn't allocate memory"
+				    " for translated lang name\n");
+				return;
+			}
+		}
+	}
+}