/*
* lang.c - determines language, handles translations
*
* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Author(s): Erik Troan
* Matt Wilson
* Michael Fulbright
* Jeremy Katz
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "loader.h"
#include "lang.h"
#include "loadermisc.h"
#include "windows.h"
#include "unpack.h"
#include "../pyanaconda/isys/lang.h"
#include "../pyanaconda/isys/isys.h"
#include "../pyanaconda/isys/log.h"
const char *LANG_DEFAULT = "en_US.UTF-8";
/* boot flags */
extern uint64_t flags;
struct aString {
unsigned int hash;
short length;
char * str;
} ;
struct aString * strings = NULL;
int numStrings = 0, allocedStrings = 0;
static int english = 0;
static char * topLineWelcome = N_("Welcome to %s for %s");
static char * topLineWelcomeRescue = N_("Welcome to %s for %s - Rescue Mode");
static char * bottomHelpLine = N_(" / between elements | selects | next screen ");
static int aStringCmp(const void * a, const void * b) {
const struct aString * first = a;
const struct aString * second = b;
if (first->hash < second->hash)
return -1;
else if (first->hash == second->hash)
return 0;
return 1;
}
char * translateString(char * str) {
unsigned int sum = 0, xor = 0;
int len = 0;
char * chptr;
struct aString * match;
struct aString key;
for (chptr = str; *chptr; chptr++) {
sum += *chptr;
xor ^= *chptr;
len++;
}
key.hash = (sum << 16) | ((xor & 0xFF) << 8) | (len & 0xFF);
match = bsearch(&key, strings, numStrings, sizeof(*strings), aStringCmp);
if (!match)
return str;
return match->str;
}
static struct langInfo * languages = NULL;
static int numLanguages = 0;
static void loadLanguageList(void) {
char * file = "/etc/lang-table";
FILE * f;
char line[256];
char name[256], key[256], font[256], code[256],
keyboard[256], timezone[256];
int lineNum = 0;
wcwidth(0);
f = fopen(file, "r");
if (!f) {
newtWinMessage(_("Error"), _("OK"), "cannot open %s: %m", file);
return;
}
while (fgets(line, sizeof(line), f)) {
lineNum++;
languages = realloc(languages, sizeof(*languages) * (numLanguages + 1));
if (sscanf(line, "%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\n",
name, key, font, code, keyboard, timezone) != 6) {
printf("bad line %d in lang-table", lineNum);
logMessage(WARNING, "bad line %d in lang-table", lineNum);
} else {
languages[numLanguages].lang = strdup(name);
languages[numLanguages].key = strdup(key);
languages[numLanguages].font = strdup(font);
languages[numLanguages].lc_all = strdup(code);
languages[numLanguages++].keyboard = strdup(keyboard);
}
}
fclose(f);
}
int getLangInfo(struct langInfo ** langs) {
if (!languages)
loadLanguageList();
*langs = languages;
return numLanguages;
}
void loadLanguage(void)
{
char *filename;
int fd, hash, rc;
char * key = getenv("LANGKEY");
if (strings) {
free(strings), strings = NULL;
numStrings = allocedStrings = 0;
}
/* english requires no files */
if (!strcmp(key, "en"))
return;
checked_asprintf(&filename, "/tmp/translation/%s.tr", key);
rc = unpack_archive_file("/etc/loader.tr", "/tmp/translation");
if (rc != ARCHIVE_OK || access("/tmp/translation", R_OK) == -1) {
newtWinMessage("Error", "OK", "Cannot get translation file %s.\n",
filename);
return;
}
fd = open(filename, O_RDONLY);
if (fd < 0) {
newtWinMessage("Error", "OK", "Failed to open /tmp/translation: %m\n");
free(filename);
return;
}
while (read(fd, &hash, 4) == 4) {
if (allocedStrings == numStrings) {
allocedStrings += 10;
strings = realloc(strings, sizeof(*strings) * allocedStrings);
}
strings[numStrings].hash = ntohl(hash);
rc = read(fd, &strings[numStrings].length, 2);
strings[numStrings].length = ntohs(strings[numStrings].length);
strings[numStrings].str = malloc(strings[numStrings].length + 1);
rc = read(fd, strings[numStrings].str, strings[numStrings].length);
strings[numStrings].str[strings[numStrings].length] = '\0';
numStrings++;
}
close(fd);
free(filename);
int translation_dir_fd = open("/tmp/translation", O_RDONLY);
recursiveRemove(translation_dir_fd);
close(translation_dir_fd);
rmdir("/tmp/translation");
qsort(strings, numStrings, sizeof(*strings), aStringCmp);
}
/* give the index of the language to set to -- sets the appropriate
* lang variables if we have a font.
*
* ASSUMPTION: languages exists
*/
static void setLangEnv (int i) {
if (i > numLanguages)
return;
if (strcmp(languages[i].font, "latarcyrheb-sun16"))
return;
logMessage(INFO, "setting language to %s", languages[i].lc_all);
setenv("LANG", languages[i].lc_all, 1);
setenv("LANGKEY", languages[i].key, 1);
setenv("LINGUAS", languages[i].lang, 1);
loadLanguage();
}
/* choice is the index of the chosen language in languages */
static int setupLanguage(int choice, int forced) {
char * buf;
char *locale_def = NULL; /* locale lang name */
char *locale_charset = NULL;/* locale charset */
char *locale_mod = NULL; /* locale variant */
char *locale_p = NULL; /* last known locale separator */
char *locale_i = NULL; /* string iterator */
int i;
pid_t localedef_pid;
logMessage(DEBUGLVL, "going to set language to %s", languages[choice].lc_all);
/* Parse locale parts out of locale name.
Locale examples:
en
en_US
cz_CS.UTF-8
cz_CS.UTF-8@latin
*/
locale_p = locale_i = languages[choice].lc_all;
while (1) {
// we found new separator
if (*locale_i == '.' || *locale_i == '@' || *locale_i == '\0') {
// last separator was annotating charset
if (*locale_p == '.') locale_charset = strndup(locale_p + 1, locale_i - locale_p - 1);
// last separator was annotating modifier
else if (*locale_p == '@') locale_mod = strndup(locale_p + 1, locale_i - locale_p - 1);
// there was no known separator last time
else locale_def = strndup(locale_p, locale_i - locale_p);
// save last known separator
locale_p = locale_i;
if(*locale_i == '\0') break; // end of the string
}
// test next character
locale_i++;
}
logMessage(DEBUGLVL, "locale %s: base: %s, mod: %s, charset: %s", languages[choice].lc_all, locale_def, locale_mod, locale_charset);
/* prepare locale name without charset */
i = strlen(locale_def);
if (locale_mod) i+= strlen(locale_mod) + 1; /* +1 for the @ char */
locale_p = (char*)calloc(i+1, sizeof(char));
locale_p = strcpy(locale_p, locale_def);
if (locale_mod){
locale_p[strlen(locale_p)] = '@';
locale_p = strcat(locale_p, locale_mod);
}
/* generate locale record */
logMessage(INFO, "going to prepare locales for %s (locale: %s, charset: %s)", languages[choice].lc_all, locale_p, locale_charset);
if ((localedef_pid = fork()) == 0) {
execl("/usr/bin/localedef", "localedef",
"-i", locale_p,
"-f", (locale_charset) ? locale_charset : "UTF-8",
languages[choice].lc_all, NULL);
_exit(254);
}
if (localedef_pid < 0) logMessage(ERROR, "failed forking localedef for %s", languages[choice].lc_all);
else{
waitpid(localedef_pid, &i, 0);
if (WEXITSTATUS(i) != 0) logMessage(ERROR, "failed preparing locales %s [%d]", languages[choice].lc_all, WEXITSTATUS(i));
}
/* cleanup */
if (locale_charset) free(locale_charset);
if (locale_def) free(locale_def);
if (locale_mod) free(locale_mod);
if (locale_p) free(locale_p);
/* load the language only if it is displayable. if they're using
* a serial console or iSeries vioconsole, we hope it's smart enough */
if ((strcmp(languages[choice].font, "latarcyrheb-sun16") && !FL_SERIAL(flags) &&
!FL_VIRTPCONSOLE(flags) && !isVioConsole())) {
if (forced == 1) return 0;
newtWinMessage("Language Unavailable", "OK",
"%s display is unavailable in text mode. The "
"installation will continue in English until the "
"display of %s is possible.", languages[choice].lang,
languages[choice].lang);
setLangEnv(english);
return 0;
}
setLangEnv (choice);
isysLoadFont();
/* clear out top line */
buf = alloca(81); /* reserve one byte for \0 */
for (i=0; i < 80; i++)
buf[i] = ' ';
buf[80] = 0; /* and set the \0 */
newtDrawRootText(0, 0, buf);
char *fmt = FL_RESCUE(flags) ? _(topLineWelcomeRescue) : _(topLineWelcome);
checked_asprintf(&buf, fmt, getProductName(), getProductArch());
newtDrawRootText(0, 0, buf);
free(buf);
newtPopHelpLine();
newtPushHelpLine(_(bottomHelpLine));
return 0;
}
/* this is pretty simple. we want to break down the language specifier
* into its short form (eg, en_US)
*/
static char * getLangShortForm(char * oldLang) {
char * lang;
char * c;
lang = strdup(oldLang);
c = strchr(lang, '@');
if (c) {
*c = '\0';
}
c = strchr(lang, '.');
if (c) {
*c = '\0';
}
return lang;
}
/* return the nick of a language -- eg en_US -> en */
static char * getLangNick(char * oldLang) {
char * lang;
char * c;
lang = strdup(oldLang);
c = strchr(lang, '_');
if (c) {
*c = '\0';
}
return lang;
}
int setLanguage (const char * key, int forced) {
int i;
if (!languages) loadLanguageList();
for (i = 0; i < numLanguages; i++) {
if (!strcmp(languages[i].lc_all, key)) {
return setupLanguage(i, forced | !FL_KICKSTART(flags));
}
}
/* we didn't specify anything that's exactly in the lang-table. check
* against short forms and nicks */
for (i = 0; i < numLanguages; i++) {
if (!strcmp(getLangShortForm(languages[i].lc_all), key)) {
return setupLanguage(i, forced | !FL_KICKSTART(flags));
}
}
for (i = 0; i < numLanguages; i++) {
if (!strcmp(getLangNick(languages[i].lc_all), key)) {
return setupLanguage(i, forced | !FL_KICKSTART(flags));
}
}
logMessage(ERROR, "unable to set to requested language %s", key);
return -1;
}
int chooseLanguage(char ** lang) {
int choice = 0;
char ** langs;
int i;
int current = -1;
char * currentLangName = getenv("LANG");
int numLangs = 0;
char * langPicked;
if (!languages) loadLanguageList();
langs = alloca(sizeof(*langs) * (numLanguages + 1));
for (i = 0; i < numLanguages; i++) {
if (!strncmp(languages[i].key, "en", 2))
english = numLangs;
if (currentLangName &&
!strcmp(languages[i].lang, currentLangName))
current = numLangs;
langs[numLangs++] = languages[i].lang;
}
langs[numLangs] = NULL;
if (current >= 0)
choice = current;
else
choice = english;
if (!FL_CMDLINE(flags))
newtWinMenu(_("Choose a Language"),
_("What language would you like to use during the "
"installation process?"), 40, 5, 5, 8,
langs, &choice, _("OK"), NULL);
langPicked = langs[choice];
for (i = 0; i < numLanguages; i++) {
if (!strcmp(langPicked, languages[i].lang)) {
*lang = languages[i].lc_all;
choice = i;
break;
}
}
/* this can't happen */
if (i == numLanguages) abort();
return setupLanguage(choice, 0);
}