diff -r 000000000000 -r 87f703f8362e usr/src/lib/liborchestrator/locale.c --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + } + } + } +}