399 lines
10 KiB
C
399 lines
10 KiB
C
|
/*
|
||
|
* 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 <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* Author(s): Erik Troan <ewt@redhat.com>
|
||
|
* Matt Wilson <msw@redhat.com>
|
||
|
* Michael Fulbright <msf@redhat.com>
|
||
|
* Jeremy Katz <katzj@redhat.com>
|
||
|
*/
|
||
|
|
||
|
#include <alloca.h>
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <newt.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <unistd.h>
|
||
|
#include <wchar.h>
|
||
|
|
||
|
#include "loader.h"
|
||
|
#include "lang.h"
|
||
|
#include "log.h"
|
||
|
#include "loadermisc.h"
|
||
|
#include "windows.h"
|
||
|
|
||
|
#include "../isys/stubs.h"
|
||
|
#include "../isys/cpio.h"
|
||
|
#include "../isys/lang.h"
|
||
|
#include "../isys/isys.h"
|
||
|
|
||
|
/* 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_(" <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> 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 (char * file) {
|
||
|
char filename[200];
|
||
|
gzFile stream;
|
||
|
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;
|
||
|
|
||
|
if (!file) {
|
||
|
file = filename;
|
||
|
sprintf(filename, "/etc/loader.tr");
|
||
|
}
|
||
|
|
||
|
stream = gunzip_open(file);
|
||
|
|
||
|
if (!stream) {
|
||
|
newtWinMessage("Error", "OK", "Translation for %s is not available. "
|
||
|
"The Installation will proceed in English.", key);
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
sprintf(filename, "%s.tr", key);
|
||
|
|
||
|
rc = installCpioFile(stream, filename, "/tmp/translation", 1);
|
||
|
gunzip_close(stream);
|
||
|
|
||
|
if (rc || access("/tmp/translation", R_OK)) {
|
||
|
newtWinMessage("Error", "OK", "Cannot get translation file %s.\n",
|
||
|
filename);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
fd = open("/tmp/translation", O_RDONLY);
|
||
|
if (fd < 0) {
|
||
|
newtWinMessage("Error", "OK", "Failed to open /tmp/translation: %m\n");
|
||
|
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);
|
||
|
unlink("/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 (NULL);
|
||
|
}
|
||
|
|
||
|
/* choice is the index of the chosen language in languages */
|
||
|
static int setupLanguage(int choice, int forced) {
|
||
|
char * buf;
|
||
|
int i;
|
||
|
|
||
|
logMessage(DEBUGLVL, "going to set language to %s", languages[choice].lc_all);
|
||
|
/* 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(80);
|
||
|
for (i=0; i < 80; i++)
|
||
|
buf[i] = ' ';
|
||
|
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 (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);
|
||
|
}
|
||
|
|
||
|
void setKickstartLanguage(struct loaderData_s * loaderData, int argc,
|
||
|
char ** argv) {
|
||
|
if (argc < 2) {
|
||
|
logMessage(ERROR, "no argument passed to lang kickstart command");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
loaderData->lang = argv[1];
|
||
|
loaderData->lang_set = 1;
|
||
|
}
|