blob: 8c8167dad915264ef98f50c8ae9644d95776d53b [file] [log] [blame]
/***************************************************************************
*
* localedef.cpp
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Copyright 2001-2006 Rogue Wave Software.
*
**************************************************************************/
#include "aliases.h"
#include "def.h" // for Def
#include "diagnostic.h" // for issue_diag()
#include "loc_exception.h" // for loc_exception
#include "path.h"
#include <fstream> // for ifstream
#include <iostream> // for cerr
#include <string> // for string
#include <vector> // for vector
#include <clocale> // for setlocale(), LC_XXX
#include <cstdlib> // for atoi()
#include <cstring> // for strcmp()
// exit status
#define EXIT_OK 0
#define EXIT_ERRORS 4
// charmaps holds pointers to the character maps being used to generate
// the locales.
static std::vector<Charmap*> charmaps;
static void
print_help_msg ()
{
static const char msg[] =
"NAME\n"
"\tlocaledef - generate a locale environment\n"
"\n"
"SYNOPSIS\n"
"\tlocaledef [-c][-w[###]][-f charmap_file][-i locale_defintion]\n"
"\t\tlocale_name\n"
"\n"
"\tlocaledef -g [-m creation_list]\n"
"\t\t[-d output_dir][-r charmap_dir][-s src_dir][--ucs]\n"
"\n"
"DESCRIPTION\n"
"\tThe localedef utility sets up the language environment for the\n"
"\tnamed locale. localedef reads a locale definition file from\n"
"\tstandard input or from locale_definition file and creates a\n"
"\tlocale database with the name specified on the command line.\n"
"\n"
"OPTIONS\n"
"\tThe localedef utility recognizes the following options:\n"
"\n"
"\t-c\tCreate output irrespective of warnings.\n"
"\n"
"\t-f charmap_file\n"
"\t\tIf the locale definition uses symbolic names use charmap_file.\n"
"\n"
"\t-i locale_definition\n"
"\t\tUse locale_definition as input, instead of standard input\n"
"\t\t(default).\n"
"\n"
"\t-g\tGenerate all the locales listed in the creation list.\n"
"\n"
"\t--ucs\tUse UCS for the internal (wchar_t) encoding."
"\n"
"\n\t-m creation_list\n"
"\t\tSpecify a file that lists what combinations of charmaps and\n"
"\t\tsource files should be used to create a locale.\n"
"\n"
"\t-r charmap_dir\n"
"\t\tSpecify the directory where the character maps are located.\n"
"\n"
"\t-s src_dir\n"
"\t\tSpecify the directory where the source files are located.\n"
"\n"
"\t-d output_dir\n"
"\t\tSpecify the directory where the generated locale database\n"
"\t\tfiles should be placed.\n"
"\n"
"\t-w\tDisable all warning messages.\n"
"\n"
"\t-w###\n"
"\t\tDisable warning number ###.\n"
"\n"
"\t--notes\tEnable notes (informational messages).\n"
"\t-?\n"
"\t--help\tPrint out this message.\n";
std::cout << msg;
}
static void
create_locale (std::string std_src,
std::string std_cmap,
std::string outdir,
std::string std_locale,
bool force_output, bool use_ucs,
bool no_position, bool link_aliases)
{
// extract the names of the locale and of the codeset
std::string lname (std_src);
std::string cname (std_cmap);
if (lname.rfind(_RWSTD_PATH_SEP) != std::string::npos)
lname = lname.substr(lname.rfind(_RWSTD_PATH_SEP) + 1, lname.size());
if (cname.rfind(_RWSTD_PATH_SEP) != std::string::npos)
cname = cname.substr(cname.rfind(_RWSTD_PATH_SEP) + 1, cname.size());
if (lname.find('.') != std::string::npos)
lname = lname.substr(0, lname.find('.'));
if (cname.find('.') != std::string::npos)
cname = cname.substr(0, cname.find('.'));
// the vector of corresponding C locales
StringVector C_locales;
#ifndef _MSC_VER
get_same_encoding_C_locale (lname, cname, C_locales);
#endif // _MSC_VER
// C library locale using same encoding
std::string enc_C_locale;
#ifdef _RWSTD_NO_ISO_10646_WCHAR_T
// the encoding C locale
enc_C_locale = get_C_encoding_locale (cname);
// platforms with locale dependant wchar_t encodings need the current
// C locale to be set. If there is no C locale with the same name
// or that uses the same encoding as the locale we are creating
// issue warning
if (enc_C_locale.empty ()) {
issue_diag (W_COMPAT, false, 0, "no compatible locale found\n");
} else
std::setlocale (LC_ALL, enc_C_locale.c_str ());
#endif // _RWSTD_NO_ISO_10646_WCHAR_T
// if no charmap is present assume ISO-8859-1
if (std_cmap.empty ())
std_cmap = "ISO-8859-1";
// retrieve UTF-8 encoding aliases
std::string utf8_cname("UTF-8");
StringVector utf8_aliases;
get_cname_aliases (utf8_cname, utf8_aliases);
// is it a UTF-8 encoded locale?
bool is_utf8 = false;
StringVector::iterator pos = utf8_aliases.begin();
for (; pos != utf8_aliases.end (); pos++)
if (ci_compare (cname, *pos) == 0) {
is_utf8 = true;
break;
}
// retrieve the charmap/codeset object
Charmap* charmap_p = 0;
std::vector <Charmap*>::iterator charmaps_it = charmaps.begin();
for (; charmaps_it != charmaps.end(); charmaps_it++){
if ((*charmaps_it)->get_full_charmap_name() == std_cmap) {
charmap_p = *charmaps_it;
break;
}
}
// if none found, create one and parse the corresponding file
if (0 == charmap_p) {
issue_diag (I_STAGE, false, 0,
"processing character set description file %s\n",
std_cmap.c_str ());
charmap_p = new Charmap (enc_C_locale.c_str (),
std_cmap.c_str (),
is_utf8, true, true, use_ucs);
charmaps.push_back (charmap_p);
}
// parse the source definition files
bool def_error = false;
issue_diag (I_STAGE, false, 0,
"processing locale definition file %s\n", std_src.c_str ());
Def def (std_src.c_str (), (outdir + std_locale).c_str (),
*charmap_p, no_position);
try {
// try to parse the input files
def.process_input ();
}
catch (...) {
def_error = true;
}
// create the locale directory
std::string locale_dir (outdir + std_locale);
makedir (locale_dir.c_str ());
if (def_error) {
// write out the codecvt database and exit if parsing failed
def.write_codecvt (locale_dir);
throw loc_exception ("abort.");
}
// no output when it hasn't been forced and warnings were present
if (!force_output && def.warnings_occurred_) {
std::cerr << "Warnings occurred - No output produced\n";
return;
}
// and write out the locale categories data
issue_diag (I_STAGE, false, 0, "generating LC_CTYPE database\n");
def.write_ctype (locale_dir);
issue_diag (I_STAGE, false, 0, "generating codeset database\n");
def.write_codecvt (locale_dir);
issue_diag (I_STAGE, false, 0, "generating LC_MONETARY database\n");
def.write_monetary (locale_dir);
issue_diag (I_STAGE, false, 0, "generating LC_NUMERIC database\n");
def.write_numeric (locale_dir);
issue_diag (I_STAGE, false, 0, "generating LC_TIME database\n");
def.write_time (locale_dir);
issue_diag (I_STAGE, false, 0, "generating LC_COLLATE database\n");
def.write_collate (locale_dir);
#ifndef _MSC_VER
issue_diag (I_STAGE, false, 0, "generating LC_MESSAGES database\n");
def.write_messages (locale_dir);
#endif // _MSC_VER
// no C library locales equivalents
if (C_locales.empty ())
return;
#if !defined (_MSC_VER)
if (link_aliases == false)
return;
// some corresponding C lib locale names where found for this name
StringVector::iterator it = C_locales.begin ();
for (; it != C_locales.end (); it++) {
// check if the name actually exists
if (*it == std_locale)
continue;
// set a symlink with the name of the C lib locale
// pointing to our locale database
create_symlink (outdir, std_locale, *it);
}
#endif // _MSC_VER
}
static void
generate_locales (const char* map_name, const char* /*alias_name*/,
const char* charmap_dir, const char* src_dir,
const char* output_dir, bool use_ucs,
bool no_position, bool link_aliases)
{
std::ifstream f (map_name);
if (!f) {
issue_diag (E_OPENRD, true, 0,
"the generation list '%s' "
"could not be opened\n", map_name);
return;
}
while (f) {
std::string s1;
std::string s2;
f >> s1 >> s2;
if (f) {
std::string lname = s1.substr (0, s1.find('.'));
std::string cname = s2.substr (0, s2.find('.'));
// our name for a locale database is <locale>.<codeset>
std::string std_locale(lname + "." + cname);
// create the locale database
std::cout << "creating locale " << std_locale << '\n';
try {
create_locale ((std::string(src_dir) + s1),
(std::string(charmap_dir) + s2),
std::string (output_dir), std_locale,
true, use_ucs, no_position, link_aliases);
}
catch (const std::ios::failure& error) {
std::cerr << "I/O exception " << error.what() << '\n';
}
catch (loc_exception& e) {
std::cerr << "Unable to create locale " << std_locale
<< " : " << e.what () << '\n';
}
catch (const std::exception& error) {
std::cerr <<"ERROR: " << error.what() << '\n';
}
catch (...) {
std::cerr << "Unable to create locale " << std_locale
<< '\n';
}
}
}
}
struct ProgramOptions
{
const char* program_name;
const char* charmap_name;
const char* source_name;
const char* map_file;
const char* alias_file;
const char* locale_name;
std::string charmap_dir;
std::string src_dir;
std::string output_dir;
bool gen;
bool force_output;
bool use_ucs;
bool no_position;
bool link_aliases;
};
static bool
process_command_line (ProgramOptions *opts, int argc, char* argv[])
{
opts->program_name = argv [0];
opts->charmap_name = "";
opts->source_name = "";
opts->map_file = 0;
opts->alias_file = 0;
opts->locale_name = 0;
opts->gen = false;
opts->force_output = false;
opts->use_ucs = false;
opts->no_position = false;
opts->link_aliases = false;
int i; // index of command line argument being processed
for (i = 1; i < argc && '-' == argv [i][0]; ++i) {
switch (argv [i][1]) {
case 'f': // set character set description file name
if (argv [i + 1])
opts->charmap_name = argv [++i];
break;
case 'i': // set locale definition file name
if (argv [i + 1])
opts->source_name = argv [++i];
break;
case 'c': // create output even if warnings are issued
opts->force_output = true;
break;
case 'g': // generate more than one locale database
opts->gen = true;
break;
case 'm':
if (argv [i + 1])
opts->map_file = argv [++i];
break;
case 'a':
if (argv [i + 1])
opts->alias_file = argv [++i];
break;
case 'r':
if (argv [i + 1])
opts->charmap_dir = argv [++i];
break;
case 's':
if (argv [i + 1])
opts->src_dir = argv [++i];
break;
case 'd':
if (argv [i + 1])
opts->output_dir = argv [++i];
break;
case 'w': // disable one or all warnings
if (argv [i][2])
issue_diag (std::atoi (argv [i] + 2), false, 0, 0);
else
issue_diag (W_DISABLE, false, 0, 0);
break;
case '?':
print_help_msg ();
return false;
case '-':
if (0 == std::strcmp (argv [i] + 2, "help")) {
print_help_msg ();
return false;
}
if (0 == std::strcmp (argv [i] + 2, "ucs")) {
// --ucs: use UCS as the internal wchar_t encoding
opts->use_ucs = true;
break;
}
else if (0 == std::strcmp (argv [i] + 2, "no_position")) {
opts->no_position = true;
break;
}
else if (0 == std::strcmp (argv [i] + 2, "aliases")) {
opts->link_aliases = true;
break;
}
else if (0 == std::strcmp (argv [i] + 2, "notes")) {
// --notes: enable informational messages (notes)
issue_diag (I_ENABLE, false, 0, 0);
break;
}
// fall through
default:
issue_diag (E_CMDARG, true, 0, "invalid option %s\n", argv [i]);
}
}
if (opts->gen) {
bool errors = false;
// make sure that all the required options are specified
if (0 == opts->map_file) {
issue_diag (E_NOARG, true, 0,
"option %s requires a string argument\n", "-m");
}
if (opts->charmap_dir.empty ()) {
issue_diag (E_NOARG, true, 0,
"option %s requires a string argument\n", "-r");
}
if (opts->src_dir.empty ()) {
issue_diag (E_NOARG, true, 0,
"option %s requires a string argument\n", "-s");
}
if (opts->output_dir.empty ()) {
issue_diag (E_NOARG, true, 0,
"option %s requires a string argument\n", "-d");
}
// append a slash to the directories if the user didn't
if (opts->output_dir [opts->output_dir.size () - 1] != _RWSTD_PATH_SEP)
opts->output_dir += _RWSTD_PATH_SEP;
if (opts->src_dir [opts->src_dir.size () - 1] != _RWSTD_PATH_SEP)
opts->src_dir += _RWSTD_PATH_SEP;
if (opts->charmap_dir [opts->charmap_dir.size () - 1] != _RWSTD_PATH_SEP)
opts->charmap_dir += _RWSTD_PATH_SEP;
}
if (0 == argv [i] && !opts->gen) {
issue_diag (E_NOARG, true, 0, "missing command line argument\n");
}
opts->locale_name = argv [i];
if (argv [i + 1]) {
issue_diag (E_XARG, true, 0,
"extra command line arguments after %s\n", argv [i]);
}
return true;
}
int localedef_main (int argc, char *argv[])
{
ProgramOptions opts;
if (!process_command_line (&opts, argc, argv))
return EXIT_OK;
if (opts.gen) {
// create the locale databases as specified in generation list
generate_locales (opts.map_file, opts.alias_file,
opts.charmap_dir.c_str (),
opts.src_dir.c_str (),
opts.output_dir.c_str (),
opts.use_ucs,
opts.no_position,
opts.link_aliases);
}
else {
assert (0 != opts.locale_name);
// C++ locale name requested
std::string std_locale (opts.locale_name);
// retrieve the output directory if any
std::string std_outdir ("");
if (std_locale.rfind (_RWSTD_PATH_SEP) != std::string::npos) {
std_outdir =
std_locale.substr (
0, std_locale.rfind (_RWSTD_PATH_SEP) + 1);
std_locale =
std_locale.substr (std_locale.rfind (_RWSTD_PATH_SEP) + 1,
std_locale.size ());
}
// create the locale database
create_locale (opts.source_name, opts.charmap_name,
std_outdir, std_locale,
opts.force_output, opts.use_ucs,
opts.no_position, opts.link_aliases);
}
return EXIT_OK;
}
// defined in locale.cpp
int locale_main (int, char*[]);
int main (int argc, char *argv[])
{
int exit_status = EXIT_OK;
try {
if (1 < argc && 0 == std::strcmp (argv [1], "--locale-mode")) {
// invoked with the special option ""--locale-mode"
// to act like the locale utility
// remove argv [1] from command line
for (int i = 2; argv [i]; ++i)
argv [i - 1] = argv [i];
// NULL-terminate argv
argv [argc - 1] = 0;
exit_status = locale_main (argc - 1, argv);
}
else {
exit_status = localedef_main (argc, argv);
}
}
catch (const std::ios::failure& error) {
std::cerr << "Error: I/O exception: " << error.what () << '\n';
exit_status = EXIT_ERRORS;
}
catch (loc_exception&) {
exit_status = EXIT_ERRORS;
}
catch (const std::exception& error) {
std::cerr <<"Error: " << error.what () << '\n';
exit_status = EXIT_ERRORS;
}
catch (...) {
std::cerr << "Error: unknown exception\n";
exit_status = EXIT_ERRORS;
}
return exit_status;
}