--- /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;
+ }
+ }
+ }
+}