util/localedef.cpp (431 lines of code) (raw):

/*************************************************************************** * * 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 _WIN32 get_same_encoding_C_locale (lname, cname, C_locales); #endif // _WIN32 // 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 _WIN32 issue_diag (I_STAGE, false, 0, "generating LC_MESSAGES database\n"); def.write_messages (locale_dir); #endif // _WIN32 // no C library locales equivalents if (C_locales.empty ()) return; #if !defined (_WIN32) && !defined (__CYGWIN__) 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 // !_WIN32 && !__CYGWIN__ } 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; }