/*************************************************************************** * * monetary.cpp * * $Id: monetary.cpp 480734 2006-11-29 22:22:44Z sebor $ * *************************************************************************** * * 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 "def.h" // for Def #include "diagnostic.h" // for issue_diag() #include "path.h" // for get_pathname() #include "scanner.h" // for scanner #include // for assert() #include // for CHAR_MAX, CHAR_MIN #include // for strtol() #include // for ofstream #include // for money_base static const char lc_name[] = "LC_MONETARY"; void Def::process_monetary () { issue_diag (I_STAGE, false, 0, "processing %s section\n", lc_name); // nesting level int nesting_level = 0; mon_def_found_ = true; while ((next = scanner_.next_token ()).token != Scanner::tok_monetary) { // set to point to the integer represented as an ordinary char // corresponding to p_cs_precedes, p_sep_by_space, p_sign_posn, // or one of the n_ (or int_ versions) of the same char *pcharint = 0; typedef unsigned char UChar; long maxval = long (UChar (CHAR_MAX)); // maximum allowed value switch (next.token) { case Scanner::tok_end: next = scanner_.next_token(); if (next.token == Scanner::tok_monetary) { // end of monetary block if (nesting_level == 0) return; nesting_level--; scanner_.close (); } else issue_diag (E_SYNTAX, true, &next, "wrong section name in END directive\n"); break; case Scanner::tok_copy: { next = scanner_.next_token(); if (next.token != Scanner::tok_string) issue_diag (E_SYNTAX, true, &next, "expected string following \"copy\" directive\n"); // bump up the nesting level nesting_level++; issue_diag (I_STAGE, false, 0, "processing copy directive\n"); // open the file scanner_.open (get_pathname (strip_quotes (next.name), next.file)); // get comment char and escape char; // these informations are stored by the scanner while ((next = scanner_.next_token ()).token != Scanner::tok_monetary ){ // the LC_IDENTIFICATION section may also have a // LC_MONETARY token that will mess up the parsing if (next.token == Scanner::tok_ident) { while ((next = scanner_.next_token()).token != Scanner::tok_end ); next = scanner_.next_token(); } } break; } case Scanner::tok_int_curr_symbol: next = scanner_.next_token(); mon_st_.int_curr_symbol = convert_string (next.name); mon_st_.wint_curr_symbol = convert_wstring (next); break; case Scanner::tok_currency_symbol: next = scanner_.next_token(); mon_st_.currency_symbol = convert_string (next.name); mon_st_.wcurrency_symbol = convert_wstring (next); break; case Scanner::tok_mon_decimal_point: next = scanner_.next_token(); mon_st_.mon_decimal_point = convert_string (next.name); mon_st_.wmon_decimal_point = convert_wstring (next); break; case Scanner::tok_mon_thousands_sep: next = scanner_.next_token(); mon_st_.mon_thousands_sep = convert_string (next.name); mon_st_.wmon_thousands_sep = convert_wstring (next); break; case Scanner::tok_mon_grouping: mon_st_.mon_grouping.clear(); while ((next = scanner_.next_token()).token != Scanner::tok_nl) mon_st_.mon_grouping += std::strtol (next.name.c_str (), 0, 10); break; case Scanner::tok_positive_sign: next = scanner_.next_token(); mon_st_.positive_sign = convert_string (next.name); mon_st_.wpositive_sign = convert_wstring (next); break; case Scanner::tok_negative_sign: next = scanner_.next_token(); mon_st_.negative_sign = convert_string (next.name); mon_st_.wnegative_sign = convert_wstring (next); break; case Scanner::tok_frac_digits: pcharint = &mon_out_.frac_digits [0]; break; case Scanner::tok_int_frac_digits: pcharint = &mon_out_.frac_digits [1 /* int'l */ ]; break; case Scanner::tok_p_cs_precedes: maxval = 1; pcharint = &mon_out_.p_cs_precedes [0]; break; case Scanner::tok_p_sep_by_space: maxval = 2; pcharint = &mon_out_.p_sep_by_space [0]; break; case Scanner::tok_n_cs_precedes: maxval = 1; pcharint = &mon_out_.n_cs_precedes [0]; break; case Scanner::tok_n_sep_by_space: maxval = 2; pcharint = &mon_out_.n_sep_by_space [0]; break; case Scanner::tok_p_sign_posn: maxval = 4; pcharint = &mon_out_.p_sign_posn [0]; break; case Scanner::tok_n_sign_posn: maxval = 4; pcharint = &mon_out_.n_sign_posn [0]; break; case Scanner::tok_int_p_cs_precedes: maxval = 1; pcharint = &mon_out_.p_cs_precedes [1 /* int'l */ ]; break; case Scanner::tok_int_p_sep_by_space: maxval = 2; pcharint = &mon_out_.p_sep_by_space [1 /* int'l */ ]; break; case Scanner::tok_int_n_cs_precedes: maxval = 1; pcharint = &mon_out_.n_cs_precedes [1 /* int'l */ ]; break; case Scanner::tok_int_n_sep_by_space: maxval = 2; pcharint = &mon_out_.n_sep_by_space [1 /* int'l */ ]; break; case Scanner::tok_int_p_sign_posn: maxval = 4; pcharint = &mon_out_.p_sign_posn [1 /* int'l */ ]; break; case Scanner::tok_int_n_sign_posn: maxval = 4; pcharint = &mon_out_.n_sign_posn [1 /* int'l */ ]; break; default: break; } if (pcharint) { next = scanner_.next_token (); char *end = 0; const long val = std::strtol (next.name.c_str (), &end, 10); if (next.name.empty () || *end || val < -1 || maxval < val) { // report as errors values outside the permitted range // (-1 indicates an unspecified value for a keyword) issue_diag (E_INVAL, true, &next, "expected integer in [0, %li], got: %s\n", maxval, next.name.c_str ()); } else *pcharint = -1 == val ? CHAR_MAX : char (val); } } } void Def::create_format (char format [4], char sign_posn, char cs_precedes, char sep_by_space, bool is_positive) { switch (sign_posn) { // the international extension is not defined for this locale case CHAR_MAX: format [0] = CHAR_MAX; format [1] = CHAR_MAX; format [2] = CHAR_MAX; format [3] = CHAR_MAX; return; case 0: // if sign_posn is 0 then we change the sign to "()", if sign is // not the empty string then issue a warning if (is_positive) { if (!mon_st_.positive_sign.empty()) warnings_occurred_ = issue_diag (W_INVAL, false, 0, "invalid combination of positive_sign " "and p_sign_posn. Ignoring positive_sign") || warnings_occurred_; mon_st_.positive_sign = "()"; } else { if (!mon_st_.negative_sign.empty()) warnings_occurred_ = issue_diag (W_INVAL, false, 0, "invalid combination of negative_sign " "and n_sign_posn. Ignoring negative_sign") || warnings_occurred_; mon_st_.negative_sign = "()"; } // now construct the format format[0] = std::money_base::sign; if (cs_precedes == 0) { format[1] = std::money_base::value; format[2] = std::money_base::symbol; } else { format[1] = std::money_base::symbol; format[2] = std::money_base::value; } break; case 1: format[0] = std::money_base::sign; if (cs_precedes == 0) { format[1] = std::money_base::value; format[2] = std::money_base::symbol; } else { format[1] = std::money_base::symbol; format[2] = std::money_base::value; } break; case 2: if (cs_precedes == 0) { format[0] = std::money_base::value; format[1] = std::money_base::symbol; } else { format[0] = std::money_base::symbol; format[1] = std::money_base::value; } format[2] = std::money_base::sign; break; case 3: if (cs_precedes == 0) { format[0] = std::money_base::value; format[1] = std::money_base::sign; format[2] = std::money_base::symbol; } else { format[0] = std::money_base::sign; format[1] = std::money_base::symbol; format[2] = std::money_base::value; } break; case 4: if (cs_precedes == 0) { format[0] = std::money_base::value; format[1] = std::money_base::symbol; format[2] = std::money_base::sign; } else { format[0] = std::money_base::symbol; format[1] = std::money_base::sign; format[2] = std::money_base::value; } break; default: break; } // now put the space in the appropriate position if (sep_by_space == 0) format[3] = std::money_base::none; else if (sep_by_space == 1) { if (format[0] == std::money_base::value) { format[3] = format[2]; format[2] = format[1]; format[1] = std::money_base::space; } else if (format[2] == std::money_base::value) { format[3] = format[2]; format[2] = std::money_base::space; } else if (format[0] == std::money_base::symbol) { format[3] = format[2]; format[2] = format[1]; format[1] = std::money_base::space; } else { format[3] = format[2]; format[2] = std::money_base::space; } } else { if (format[0] == std::money_base::value) { format[3] = format[2]; format[2] = std::money_base::space; } else if (format[2] == std::money_base::value) { format[3] = format[2]; format[2] = format[1]; format[1] = std::money_base::space; } else if (format[0] == std::money_base::symbol) { format[3] = format[2]; format[2] = std::money_base::space; } else { format[3] = format[2]; format[2] = format[1]; format[1] = std::money_base::space; } } } void Def::write_monetary (std::string dir_name) { assert (!dir_name.empty()); if (mon_written_) return; if (!mon_def_found_) { issue_diag (I_SKIP, false, 0, "%s section not found, skipping\n", lc_name); return; } // write out all the information in the LC_MONETARY category (dir_name += _RWSTD_PATH_SEP) += lc_name; issue_diag (I_OPENWR, false, 0, "writing %s\n", dir_name.c_str ()); std::ofstream out (dir_name.c_str(), std::ios::binary); out.exceptions (std::ios::failbit | std::ios::badbit); _RW::__rw_punct_t punct; // calculate the offsets for the mon_punct structure punct.decimal_point_off [1] = 0; punct.thousands_sep_off [1] = punct.decimal_point_off [1] + (mon_st_.wmon_decimal_point.size () + 1) * sizeof (wchar_t); punct.decimal_point_off [0] = punct.thousands_sep_off [1] + (mon_st_.wmon_thousands_sep.size () + 1) * sizeof (wchar_t); punct.thousands_sep_off [0] = punct.decimal_point_off [0] + mon_st_.mon_decimal_point.size () + 1; punct.grouping_off = punct.thousands_sep_off [0] + mon_st_.mon_thousands_sep.size () + 1; punct.punct_ext_off = punct.grouping_off + mon_st_.mon_grouping.size () + 1; // compute the alignment requirement of any offset member const std::size_t align = sizeof punct.punct_ext_off; // align the offset of the extension struct on the required boundary const std::size_t misalign = punct.punct_ext_off % align; // compute the amount of padding between the two structs const std::size_t pad = misalign ? align - misalign : 0; punct.punct_ext_off += pad; mon_out_.curr_symbol_off [1][1] = 0; mon_out_.curr_symbol_off [0][1] = mon_out_.curr_symbol_off [1][1] + (mon_st_.wint_curr_symbol.size () + 1) * sizeof (wchar_t); mon_out_.positive_sign_off [1] = mon_out_.curr_symbol_off [0][1] + (mon_st_.wcurrency_symbol.size () + 1) * sizeof (wchar_t); mon_out_.negative_sign_off [1] = mon_out_.positive_sign_off [1] + (mon_st_.wpositive_sign.size () + 1) * sizeof (wchar_t); // calculate all the narrow character string offsets mon_out_.curr_symbol_off [1][0] = mon_out_.negative_sign_off [1] + (mon_st_.wnegative_sign.size () + 1) * sizeof (wchar_t); mon_out_.curr_symbol_off [0][0] = mon_out_.curr_symbol_off [1][0] + mon_st_.int_curr_symbol.size () + 1; mon_out_.positive_sign_off [0] = mon_out_.curr_symbol_off [0][0] + mon_st_.currency_symbol.size() + 1; mon_out_.negative_sign_off [0] = mon_out_.positive_sign_off [0] + mon_st_.positive_sign.size () + 1; mon_out_.codeset_off = mon_out_.negative_sign_off [0] + mon_st_.negative_sign.size () + 1; mon_out_.charmap_off = mon_out_.codeset_off + charmap_.get_code_set_name ().size () + 1; issue_diag (I_WRITE, false, 0, "%s layout:\n" "__rw_punct_t {\n" " decimal_point_off[] = { %u, %u }\n" " thousand_sep_off[] = { %u, %u }\n" " grouping_off = %u\n" " ext_off = %u\n" "}\n__rw_moneypunct_t {\n" " curr_symbol_off[][] = { { %u, %u }, { %u, %u } }\n" " positive_sign_off[] = { %u, %u }\n" " negative_sign_off[] = { %u, %u }\n" " codeset_off = %u\n" " charmap_off = %u\n" "}\n", lc_name, punct.decimal_point_off [0], punct.decimal_point_off [1], punct.thousands_sep_off [0], punct.thousands_sep_off [1], punct.grouping_off, punct.punct_ext_off, mon_out_.curr_symbol_off [0][0], mon_out_.curr_symbol_off [0][1], mon_out_.curr_symbol_off [1][0], mon_out_.curr_symbol_off [1][1], mon_out_.positive_sign_off [0], mon_out_.negative_sign_off [1], mon_out_.codeset_off, mon_out_.charmap_off); // construct the pos_format and neg_format create_format (mon_out_.pos_format[0], mon_out_.p_sign_posn[0], mon_out_.p_cs_precedes[0], mon_out_.p_sep_by_space[0], true); create_format (mon_out_.neg_format[0], mon_out_.n_sign_posn[0], mon_out_.n_cs_precedes[0], mon_out_.n_sep_by_space[0], false); create_format (mon_out_.pos_format[1], mon_out_.p_sign_posn[1], mon_out_.p_cs_precedes[1], mon_out_.p_sep_by_space[1], true); create_format (mon_out_.neg_format[1], mon_out_.n_sign_posn[1], mon_out_.n_cs_precedes[1], mon_out_.n_sep_by_space[1], false); // write out the mon_punct structure out.write ((char*)&punct, sizeof punct); // write out the strings in the punct structure out.write ((const char*)mon_st_.wmon_decimal_point.c_str(), (mon_st_.wmon_decimal_point.size() + 1) * sizeof(wchar_t)); out.write ((const char*)mon_st_.wmon_thousands_sep.c_str(), (mon_st_.wmon_thousands_sep.size() + 1) * sizeof(wchar_t)); out << mon_st_.mon_decimal_point << std::ends << mon_st_.mon_thousands_sep << std::ends << mon_st_.mon_grouping << std::ends; // pad the structure up to the next alignment boundary out.write ("\0\0\0\0\0\0\0\0", pad); // now write out the monetary offset extension struct out.write ((char*)&mon_out_, sizeof (mon_out_)); // write the wide character strings out.write ((const char*)mon_st_.wint_curr_symbol.c_str(), (mon_st_.wint_curr_symbol.size() + 1) * sizeof(wchar_t)); out.write ((const char*)mon_st_.wcurrency_symbol.c_str(), (mon_st_.wcurrency_symbol.size() + 1) * sizeof(wchar_t)); out.write ((const char*)mon_st_.wpositive_sign.c_str(), (mon_st_.wpositive_sign.size() + 1) * sizeof(wchar_t)); out.write ((const char*)mon_st_.wnegative_sign.c_str(), (mon_st_.wnegative_sign.size() + 1) * sizeof(wchar_t)); // write the narrow character strings out << mon_st_.int_curr_symbol << std::ends << mon_st_.currency_symbol << std::ends << mon_st_.positive_sign << std::ends << mon_st_.negative_sign << std::ends << charmap_.get_code_set_name() << std::ends << charmap_.get_charmap_name() << std::ends; }