diff options
Diffstat (limited to 'debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/option.cpp')
-rw-r--r-- | debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/option.cpp | 1325 |
1 files changed, 1325 insertions, 0 deletions
diff --git a/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/option.cpp b/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/option.cpp new file mode 100644 index 00000000..be09ac2f --- /dev/null +++ b/debian/uncrustify-trinity/uncrustify-trinity-0.78.1/src/option.cpp @@ -0,0 +1,1325 @@ +/** + * @file option.cpp + * Parses the options from the config file. + * + * @author Ben Gardner + * @author Guy Maurel October 2015, 2021 + * @author Matthew Woehlke since version 0.67 + * @license GPL v2+ + */ + +#include "option.h" + +#include "keywords.h" +#include "language_names.h" +#include "uncrustify.h" +#include "uncrustify_version.h" + +#include <fstream> +#include <unordered_map> + +#include <cctype> // to get std::tolower +#include <cstdarg> // to get va_start, va_end + + +namespace uncrustify +{ + +namespace +{ + +static const char *DOC_TEXT_END = u8R"___( +# Meaning of the settings: +# Ignore - do not do any changes +# Add - makes sure there is 1 or more space/brace/newline/etc +# Force - makes sure there is exactly 1 space/brace/newline/etc, +# behaves like Add in some contexts +# Remove - removes space/brace/newline/etc +# +# +# - Token(s) can be treated as specific type(s) with the 'set' option: +# `set tokenType tokenString [tokenString...]` +# +# Example: +# `set BOOL __AND__ __OR__` +# +# tokenTypes are defined in src/token_enum.h, use them without the +# 'CT_' prefix: 'CT_BOOL' => 'BOOL' +# +# +# - Token(s) can be treated as type(s) with the 'type' option. +# `type tokenString [tokenString...]` +# +# Example: +# `type int c_uint_8 Rectangle` +# +# This can also be achieved with `set TYPE int c_uint_8 Rectangle` +# +# +# To embed whitespace in tokenStrings use the '\' escape character, or quote +# the tokenStrings. These quotes are supported: "'` +# +# +# - Support for the auto detection of languages through the file ending can be +# added using the 'file_ext' command. +# `file_ext langType langString [langString..]` +# +# Example: +# `file_ext CPP .ch .cxx .cpp.in` +# +# langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use +# them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP' +# +# +# - Custom macro-based indentation can be set up using 'macro-open', +# 'macro-else' and 'macro-close'. +# `(macro-open | macro-else | macro-close) tokenString` +# +# Example: +# `macro-open BEGIN_TEMPLATE_MESSAGE_MAP` +# `macro-open BEGIN_MESSAGE_MAP` +# `macro-close END_MESSAGE_MAP` +# +# +)___"; + + +std::vector<OptionGroup> option_groups; +std::unordered_map<std::string, GenericOption *> option_map; + +#define LOG_CONFIG(...) \ + log_config(); LOG_FMT(LNOTE, __VA_ARGS__); + + +//----------------------------------------------------------------------------- +constexpr int option_level(int major, int minor, int patch = 0) +{ + return((major << 20) | (minor << 10) | (patch << 0)); +} + + +//----------------------------------------------------------------------------- +void log_config() +{ + // Print the name of the configuration file only once + static bool config_name_logged = false; + + if (!config_name_logged) + { + LOG_FMT(LNOTE, "log_config: the configuration file is: %s\n", + cpd.filename.c_str()); + config_name_logged = true; + } +} + + +//----------------------------------------------------------------------------- +// This identity function exists so that all Option<T>::str can simply call +// to_string(m_val); this function will be used by Option<string> +std::string to_string(const std::string &in) +{ + return(in); +} + +using std::to_string; + + +//----------------------------------------------------------------------------- +std::string to_lower(const char *in, std::string::size_type size = 0) +{ + std::string out; + + if (size > 0) + { + out.reserve(size); + } + + while (*in) + { + out += static_cast<char>(std::tolower(*in)); + ++in; + } + return(out); +} + + +//----------------------------------------------------------------------------- +std::string to_lower(const std::string &in) +{ + return(to_lower(in.data(), in.size())); +} + + +//----------------------------------------------------------------------------- +bool is_arg_sep(int ch) +{ + return( isspace(ch) + || ch == ',' + || ch == '='); +} + + +//----------------------------------------------------------------------------- +bool is_varg_sep(int ch) +{ + return(ch == '.'); +} + + +//----------------------------------------------------------------------------- +std::vector<std::string> split_args(std::string in, const char *filename, + bool (*is_sep)(int)) +{ + std::vector<std::string> out; + std::string::size_type n = 0; + std::string::size_type k = in.size(); + + // Parse input string + while (n < k) + { + // Skip leading space + while ( n < k + && is_sep(in[n])) + { + ++n; + } + + // Detect comments or trailing space + if ( n >= k + || in[n] == '#') + { + break; + } + + // Detect and extract quoted string + if (const auto *quote = strchr("\'\"`", in[n])) + { + const auto start = ++n; + + for ((void)n; in[n] != *quote; ++n) + { + if ( n < k + && in[n] == '\\') + { + in.erase(n, 1); + --k; + } + + if (n >= k) + { + OptionWarning w{ filename }; + w("found unterminated quoted-string"); + return{}; + } + } + + out.push_back(in.substr(start, n - start)); + + if ( ++n < k + && !is_sep(in[n])) + { + OptionWarning w{ filename }; + w("unexpected text following quoted-string"); + return{}; + } + continue; + } + // Extract anything else + const auto start = n; + + for ((void)n; + ( n < k + && !is_sep(in[n])); + ++n) + { + if (in[n] == '\\') + { + in.erase(n, 1); + --k; + } + + if (n >= k) + { + OptionWarning w{ filename }; + w("found unterminated quoted-string"); + return{}; + } + } + + out.push_back(in.substr(start, n - start)); + } + return(out); +} // split_args + + +//----------------------------------------------------------------------------- +bool is_path_relative(const std::string &path) +{ + assert(!path.empty()); + +#ifdef WIN32 + // Check for partition labels as indication for an absolute path + // 'X:\path\to\file' style absolute disk path + if ( path.size() > 1 + && isalpha(path[0]) + && path[1] == ':') + { + return(false); + } + + // Check for double backslashes as indication for a network path + // '\\server\path\to\file style' absolute UNC path + if ( path.size() > 1 + && path[0] == '\\' + && path[1] == '\\') + { + return(false); + } +#endif + + // Check for a slash as indication for a filename with leading path + // '/path/to/file' style absolute path + return(path[0] != '/'); +} + + +//----------------------------------------------------------------------------- +void print_description(FILE *pfile, std::string description, + const char *eol_marker) +{ + // Descriptions always start with a '\n', so skip the first character + for (std::string::size_type start = 1, length = description.length(); + ( start != std::string::npos + && start < length); + ++start) + { + // Check for empty line so we can squelch trailing whitespace + if (description[start] == '\n') + { + fprintf(pfile, "#%s", eol_marker); + } + else + { + const auto end = description.find('\n', start); + fprintf(pfile, "# %s%s", + description.substr(start, end - start).c_str(), eol_marker); + start = end; + } + } +} + + +//----------------------------------------------------------------------------- +bool process_option_line_compat_0_68(const std::string &cmd, + const std::vector<std::string> &args, + const char *filename) +{ + if (cmd == "sp_cpp_lambda_paren") + { + OptionWarning w{ filename, OptionWarning::MINOR }; + w("option '%s' is deprecated; use '%s' instead", + cmd.c_str(), options::sp_cpp_lambda_square_paren.name()); + + UNUSED(options::sp_cpp_lambda_square_paren.read(args[1].c_str())); + return(true); + } + return(false); +} // process_option_line_compat_0_68 + + +bool process_option_line_compat_0_70(const std::string &cmd, const char *filename) +{ + if (cmd == "sp_word_brace") // Issue #2428 + { + OptionWarning w{ filename, OptionWarning::MINOR }; + w("option '%s' is deprecated; did you want to use '%s' instead?", + cmd.c_str(), options::sp_type_brace_init_lst.name()); + + //UNUSED(options::sp_type_brace_init_lst.read(args[1].c_str())); + return(true); + } + return(false); +} // process_option_line_compat_0_70 + + +bool process_option_line_compat_0_73(const std::string &cmd, const char *filename) +{ + if (cmd == "indent_sing_line_comments") // Issue #3249 + { + OptionWarning w{ filename, OptionWarning::MINOR }; + w("option '%s' is deprecated; did you want to use '%s' instead?", + cmd.c_str(), options::indent_single_line_comments_before.name()); + + //UNUSED(options::indent_single_line_comments_before.read(args[1].c_str())); + return(true); + } + + if (cmd == "sp_before_tr_emb_cmt") // Issue #3339 + { + OptionWarning w{ filename, OptionWarning::MINOR }; + w("option '%s' is deprecated; did you want to use '%s' instead?", + cmd.c_str(), options::sp_before_tr_cmt.name()); + + //UNUSED(options::sp_before_tr_cmt.read(args[1].c_str())); + return(true); + } + + if (cmd == "sp_num_before_tr_emb_cmt") // Issue #3339 + { + OptionWarning w{ filename, OptionWarning::MINOR }; + w("option '%s' is deprecated; did you want to use '%s' instead?", + cmd.c_str(), options::sp_num_before_tr_cmt.name()); + + //UNUSED(options::sp_num_before_tr_cmt.read(args[1].c_str())); + return(true); + } + return(false); +} // process_option_line_compat_0_73 + + +bool process_option_line_compat_0_74(const std::string &cmd, const char *filename) +{ + if (cmd == "sp_type_question") // PR #3638 + { + OptionWarning w{ filename, OptionWarning::MINOR }; + w("option '%s' is deprecated; did you want to use '%s' instead?", + cmd.c_str(), options::sp_before_ptr_star.name()); + + return(true); + } + return(false); +} // process_option_line_compat_0_74 + + +bool process_option_line_compat_0_75(const std::string &cmd, const char *filename) +{ + if (cmd == "pp_space") + { + OptionWarning w{ filename, OptionWarning::MINOR }; + w("option '%s' is deprecated; it has been replaced by '%s'.", + cmd.c_str(), options::pp_space_after.name()); + + return(true); + } + + if (cmd == "pp_space_before") + { + OptionWarning w{ filename, OptionWarning::MINOR }; + w("option '%s' is deprecated; it was a temporary option used\n" + "during the development of version 0.76. Use '%s' and '%s' instead.", + cmd.c_str(), options::pp_indent.name(), options::pp_indent_count.name()); + + return(true); + } + return(false); +} // process_option_line_compat_0_75 + + +bool process_option_line_compat_0_76(const std::string &cmd, const std::vector<std::string> &args, const char *filename) +{ + if (cmd == "nl_func_var_def_blk") + { + OptionWarning w{ filename, OptionWarning::MINOR }; + w("option '%s' is deprecated; it has been replaced by '%s'.\n" + "You can also use '%s' for additional functionality", + cmd.c_str(), options::nl_var_def_blk_end_func_top.name(), + options::nl_var_def_blk_end.name()); + + UNUSED(options::nl_var_def_blk_end_func_top.read(args[1].c_str())); + return(true); + } + return(false); +} // process_option_line_compat_0_76 + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// + +//BEGIN Option<T> and helpers + + +//----------------------------------------------------------------------------- +OptionWarning::OptionWarning(const char *filename, Severity severity) +{ + UNUSED(severity); + + if (cpd.line_number != 0) + { + fprintf(stderr, "%s:%u: ", filename, cpd.line_number); + } + else + { + fprintf(stderr, "%s: ", filename); + } +} + + +//----------------------------------------------------------------------------- +OptionWarning::OptionWarning(const GenericOption *opt, Severity severity) +{ + UNUSED(severity); + + fprintf(stderr, "Option<%s>: at %s:%d: ", to_string(opt->type()), + cpd.filename.c_str(), cpd.line_number); +} + + +//----------------------------------------------------------------------------- +OptionWarning::~OptionWarning() +{ + fprintf(stderr, "\n"); + log_flush(true); +} + + +//----------------------------------------------------------------------------- +void OptionWarning::operator()(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + + +//----------------------------------------------------------------------------- +void GenericOption::warnUnexpectedValue(const char *actual) const +{ + OptionWarning w{ this }; + + auto values = possibleValues(); + + if (values[1] == nullptr) + { + w("Expected %s ", *values); + } + else + { + w("Expected one of "); + + while (*values) + { + w("'%s'", *values); + + if (*(++values)) + { + w(", "); + } + } + } + w(", for '%s'; got '%s'", name(), actual); +} + + +//----------------------------------------------------------------------------- +void GenericOption::warnIncompatibleReference(const GenericOption *ref) const +{ + OptionWarning w{ this }; + + w("%s references option %s with incompatible type %s", + name(), ref->name(), to_string(ref->type())); +} + + +//----------------------------------------------------------------------------- +template<typename T> +bool read_enum(const char *in, Option<T> &out) +{ + assert(in); + + if (convert_string(in, out.m_val)) + { + return(true); + } + + if (const auto *const opt = find_option(in)) + { + if (opt->type() != out.type()) + { + out.warnIncompatibleReference(opt); + return(false); + } + auto &topt = *static_cast<const Option<T> *>(opt); + out.m_val = topt(); + return(true); + } + out.warnUnexpectedValue(in); + return(false); +} + + +//----------------------------------------------------------------------------- +template<typename T> +bool read_number(const char *in, Option<T> &out) +{ + assert(in); + + char *c; + const auto val = std::strtol(in, &c, 10); + + if ( *c == 0 + && out.validate(val)) + { + out.m_val = static_cast<T>(val); + return(true); + } + bool invert = false; + + if (strchr("-", in[0])) + { + invert = true; + ++in; + } + + if (const auto *const opt = find_option(in)) + { + LOG_CONFIG("%s(%d): line_number is %d, option(%s) %s, ref(%s) %s\n", + __func__, __LINE__, cpd.line_number, + to_string(out.type()), out.name(), + to_string(opt->type()), opt->name()); + + long tval; + + if (opt->type() == OT_NUM) + { + auto &sopt = *static_cast<const Option<signed> *>(opt); + tval = static_cast<long>(sopt()); + } + else if (opt->type() == OT_UNUM) + { + auto &uopt = *static_cast<const Option<unsigned> *>(opt); + tval = static_cast<long>(uopt()); + } + else + { + out.warnIncompatibleReference(opt); + return(false); + } + const auto rval = (invert ? -tval : tval); + + if (out.validate(rval)) + { + out.m_val = static_cast<T>(rval); + return(true); + } + return(false); + } + out.warnUnexpectedValue(in); + return(false); +} // read_number + + +//----------------------------------------------------------------------------- +template<typename T> +void Option<T>::reset() +{ + m_val = m_default; +} + + +//----------------------------------------------------------------------------- +template<typename T> +std::string Option<T>::str() const +{ + return(to_string(m_val)); +} + + +//----------------------------------------------------------------------------- +template<typename T> +std::string Option<T>::defaultStr() const +{ + return(m_default != T{} ? to_string(m_default) : std::string{}); +} + +// Explicit instantiations +template class Option<bool>; +template class Option<iarf_e>; +template class Option<line_end_e>; +template class Option<token_pos_e>; +template class Option<signed>; +template class Option<unsigned>; +template class Option<std::string>; + +//END Option<T> and helpers + +/////////////////////////////////////////////////////////////////////////////// + +//BEGIN Option<bool> + + +//----------------------------------------------------------------------------- +template<> +option_type_e Option<bool>::type() const +{ + return(OT_BOOL); +} + + +//----------------------------------------------------------------------------- +template<> +const char *const *Option<bool>::possibleValues() const +{ + static char const *values[] = { "true", "false", nullptr }; + + return(values); +} + + +//----------------------------------------------------------------------------- +template<> +bool Option<bool>::read(const char *in) +{ + assert(in); + + if (convert_string(in, m_val)) + { + return(true); + } + bool invert = false; + + if (strchr("~!-", in[0])) + { + invert = true; + ++in; + } + + if (const auto *const opt = find_option(in)) + { + if (opt->type() != OT_BOOL) + { + warnIncompatibleReference(opt); + return(false); + } + auto &bopt = *static_cast<const Option<bool> *>(opt); + m_val = (invert ? !bopt() : bopt()); + return(true); + } + warnUnexpectedValue(in); + return(false); +} + +//END Option<bool> + +/////////////////////////////////////////////////////////////////////////////// + +//BEGIN Option<iarf_e> + + +//----------------------------------------------------------------------------- +template<> +option_type_e Option<iarf_e>::type() const +{ + return(OT_IARF); +} + + +//----------------------------------------------------------------------------- +template<> +const char *const *Option<iarf_e>::possibleValues() const +{ + return(iarf_values); +} + + +//----------------------------------------------------------------------------- +template<> +bool Option<iarf_e>::read(const char *in) +{ + return(read_enum(in, *this)); +} + +//END Option<iarf_e> + +/////////////////////////////////////////////////////////////////////////////// + +//BEGIN Option<line_end_e> + + +//----------------------------------------------------------------------------- +template<> +option_type_e Option<line_end_e>::type() const +{ + return(OT_LINEEND); +} + + +//----------------------------------------------------------------------------- +template<> +const char *const *Option<line_end_e>::possibleValues() const +{ + return(line_end_values); +} + + +//----------------------------------------------------------------------------- +template<> +bool Option<line_end_e>::read(const char *in) +{ + return(read_enum(in, *this)); +} + +//END Option<line_end_e> + +/////////////////////////////////////////////////////////////////////////////// + +//BEGIN Option<token_pos_e> + + +//----------------------------------------------------------------------------- +template<> +option_type_e Option<token_pos_e>::type() const +{ + return(OT_TOKENPOS); +} + + +//----------------------------------------------------------------------------- +template<> +const char *const *Option<token_pos_e>::possibleValues() const +{ + return(token_pos_values); +} + + +//----------------------------------------------------------------------------- +template<> +bool Option<token_pos_e>::read(const char *in) +{ + return(read_enum(in, *this)); +} + +//END Option<token_pos_e> + +/////////////////////////////////////////////////////////////////////////////// + +//BEGIN Option<signed> + + +//----------------------------------------------------------------------------- +template<> +option_type_e Option<signed>::type() const +{ + return(OT_NUM); +} + + +//----------------------------------------------------------------------------- +template<> +const char *const *Option<signed>::possibleValues() const +{ + static char const *values[] = { "number", nullptr }; + + return(values); +} + + +//----------------------------------------------------------------------------- +template<> +bool Option<signed>::read(const char *in) +{ + return(read_number(in, *this)); +} + +//END Option<signed> + +/////////////////////////////////////////////////////////////////////////////// + +//BEGIN Option<unsigned> + + +//----------------------------------------------------------------------------- +template<> +option_type_e Option<unsigned>::type() const +{ + return(OT_UNUM); +} + + +//----------------------------------------------------------------------------- +template<> +const char *const *Option<unsigned>::possibleValues() const +{ + static char const *values[] = { "unsigned number", nullptr }; + + return(values); +} + + +//----------------------------------------------------------------------------- +template<> +bool Option<unsigned>::read(const char *in) +{ + return(read_number(in, *this)); +} + +//END Option<unsigned> + +/////////////////////////////////////////////////////////////////////////////// + +//BEGIN Option<string> + + +//----------------------------------------------------------------------------- +template<> +option_type_e Option<std::string>::type() const +{ + return(OT_STRING); +} + + +//----------------------------------------------------------------------------- +template<> +const char *const *Option<std::string>::possibleValues() const +{ + static char const *values[] = { "string", nullptr }; + + return(values); +} + + +//----------------------------------------------------------------------------- +template<> +bool Option<std::string>::read(const char *in) +{ + m_val = in; + return(true); +} + +//END Option<string> + +/////////////////////////////////////////////////////////////////////////////// + +//BEGIN global functions for options + + +//----------------------------------------------------------------------------- +void begin_option_group(const char *description) +{ + auto g = OptionGroup{ description, {} }; + + option_groups.push_back(g); +} + + +//----------------------------------------------------------------------------- +void register_option(GenericOption *option) +{ + assert(!option_groups.empty()); + + option_groups.back().options.push_back(option); + option_map.emplace(option->name(), option); +} + + +//----------------------------------------------------------------------------- +uncrustify::GenericOption *find_option(const char *name) +{ + const auto iter = option_map.find(to_lower(name)); + + if (iter != option_map.end()) + { + return(iter->second); + } + return(nullptr); +} + + +//----------------------------------------------------------------------------- +OptionGroup *get_option_group(size_t i) +{ + if (i >= option_groups.size()) + { + return(nullptr); + } + return(&option_groups[i]); +} + + +//----------------------------------------------------------------------------- +size_t get_option_count() +{ + return(option_map.size()); +} + + +//----------------------------------------------------------------------------- +void process_option_line(const std::string &config_line, const char *filename, + int &compat_level) +{ + // Split line into arguments, and punt if no arguments are present + auto args = split_args(config_line, filename, is_arg_sep); + + if (args.empty()) + { + return; + } + // Check for necessary arguments + const auto &cmd = to_lower(args.front()); + + if ( cmd == "set" + || cmd == "file_ext") + { + if (args.size() < 3) + { + OptionWarning w{ filename }; + w("%s requires at least three arguments", cmd.c_str()); + return; + } + } + else + { + if (args.size() < 2) + { + OptionWarning w{ filename }; + w("%s requires at least two arguments", cmd.c_str()); + return; + } + } + + if (cmd == "type") + { + for (size_t i = 1; i < args.size(); ++i) + { + add_keyword(args[i], CT_TYPE); + } + } + else if (cmd == "macro-open") + { + add_keyword(args[1], CT_MACRO_OPEN); + } + else if (cmd == "macro-close") + { + add_keyword(args[1], CT_MACRO_CLOSE); + } + else if (cmd == "macro-else") + { + add_keyword(args[1], CT_MACRO_ELSE); + } + else if (cmd == "set") + { + const auto token = find_token_name(args[1].c_str()); + + if (token != CT_NONE) + { + LOG_FMT(LNOTE, "%s:%d set '%s':", + filename, cpd.line_number, args[1].c_str()); + + for (size_t i = 2; i < args.size(); ++i) + { + LOG_FMT(LNOTE, " '%s'", args[i].c_str()); + add_keyword(args[i], token); + } + + LOG_FMT(LNOTE, "\n"); + } + else + { + OptionWarning w{ filename }; + w("%s: unknown type '%s'", cmd.c_str(), args[1].c_str()); + } + } +#ifndef EMSCRIPTEN + else if (cmd == "include") + { + auto this_line_number = cpd.line_number; + const auto &include_path = args[1]; + + if (include_path.empty()) + { + OptionWarning w{ filename }; + w("include: path cannot be empty"); + } + else if (is_path_relative(include_path)) + { + // include is a relative path to the current config file + UncText ut = std::string{ filename }; + ut.resize(static_cast<unsigned>(path_dirname_len(filename))); + ut.append(include_path); + UNUSED(load_option_file(ut.c_str(), compat_level)); + } + else + { + // include is an absolute path + UNUSED(load_option_file(include_path.c_str(), compat_level)); + } + cpd.line_number = this_line_number; + } +#endif + else if (cmd == "file_ext") + { + auto *const lang_arg = args[1].c_str(); + + for (size_t i = 2; i < args.size(); ++i) + { + auto *const lang_name = extension_add(args[i].c_str(), lang_arg); + + if (lang_name) + { + LOG_FMT(LNOTE, "%s:%d file_ext '%s' => '%s'\n", + filename, cpd.line_number, args[i].c_str(), lang_name); + } + else + { + OptionWarning w{ filename }; + w("file_ext: unknown language '%s'", lang_arg); + break; + } + } + } + else if (cmd == "using") + { + auto vargs = split_args(args[1], filename, is_varg_sep); + + if (vargs.size() == 2) + { + compat_level = option_level(std::stoi(vargs[0]), std::stoi(vargs[1])); + } + else if (vargs.size() == 3) + { + compat_level = option_level(std::stoi(vargs[0]), + std::stoi(vargs[1]), + std::stoi(vargs[2])); + } + else + { + OptionWarning w{ filename }; + w("%s requires a version number in the form MAJOR.MINOR[.PATCH]", + cmd.c_str()); + } + } + else + { + // Must be a regular option = value + if (compat_level < option_level(0, 69)) + { + if (process_option_line_compat_0_68(cmd, args, filename)) + { + return; + } + } + + if (compat_level < option_level(0, 71)) + { + if (process_option_line_compat_0_70(cmd, filename)) + { + return; + } + } + + if (compat_level < option_level(0, 74)) + { + if (process_option_line_compat_0_73(cmd, filename)) + { + return; + } + } + + if (compat_level < option_level(0, 75)) + { + if (process_option_line_compat_0_74(cmd, filename)) + { + return; + } + } + + if (compat_level < option_level(0, 76)) + { + if (process_option_line_compat_0_75(cmd, filename)) + { + return; + } + } + + if (compat_level < option_level(0, 77)) + { + if (process_option_line_compat_0_76(cmd, args, filename)) + { + return; + } + } + const auto oi = option_map.find(cmd); + + if (oi == option_map.end()) + { + OptionWarning w{ filename }; + w("unknown option '%s'", args[0].c_str()); + } + else + { + UNUSED(oi->second->read(args[1].c_str())); + } + } +} // process_option_line + + +//----------------------------------------------------------------------------- +bool load_option_file(const char *filename, int compat_level) +{ + cpd.line_number = 0; + +#ifdef WIN32 + // "/dev/null" not understood by "fopen" in Windows + if (strcasecmp(filename, "/dev/null") == 0) + { + return(true); + } +#endif + + std::ifstream in; + in.open(filename, std::ifstream::in); + + if (!in.good()) + { + OptionWarning w{ filename }; + w("file could not be opened: %s (%d)\n", + strerror(errno), errno); + exit(EX_SOFTWARE); + } + // Read in the file line by line + std::string line; + + while (std::getline(in, line)) + { + // check all characters of the line + size_t howmany = line.length(); + int ch; + + for (size_t n = 0; n < howmany; n++) + { + ch = line[n]; + + // do not check characters in comment part of line + if ('#' == ch) + { + break; + } + + // ch >= 0 && ch <= 255 + if ( ch < 0 + || ch > 255) + { + // error + // related to PR #3298 + fprintf(stderr, "%s: line %u: Character at position %zu, is not printable.\n", filename, cpd.line_number + 1, n + 1); + exit(EX_SOFTWARE); + } + } + + ++cpd.line_number; + process_option_line(line, filename, compat_level); + } + return(true); +} // load_option_file + + +//----------------------------------------------------------------------------- +const char *get_eol_marker() +{ + static char eol[3] = { 0x0A, 0x00, 0x00 }; + + const auto &lines = cpd.newline.get(); + + for (size_t i = 0; i < lines.size(); ++i) + { + eol[i] = static_cast<char>(lines[i]); + } + + return(eol); +} + + +//----------------------------------------------------------------------------- +void save_option_file(FILE *pfile, bool with_doc, bool minimal) +{ + int non_default_values = 0; + const char *eol_marker = get_eol_marker(); + + fprintf(pfile, "# %s%s", UNCRUSTIFY_VERSION, eol_marker); + + // Print the options by group + for (auto &og : option_groups) + { + bool first = true; + + for (auto *option : og.options) + { + const auto val = option->str(); + + if (!option->isDefault()) + { + ++non_default_values; + } + else if (minimal) + { + continue; + } + //.................................................................... + + if (with_doc) + { + assert(option->description() != nullptr); + assert(*option->description() != 0); + + if (first) + { + fprintf(pfile, "%s#%s", eol_marker, eol_marker); + print_description(pfile, og.description, eol_marker); + fprintf(pfile, "#%s", eol_marker); + } + fprintf(pfile, "%s", eol_marker); + print_description(pfile, option->description(), eol_marker); + + const auto ds = option->defaultStr(); + + if (!ds.empty()) + { + fprintf(pfile, "#%s# Default: %s%s", + eol_marker, ds.c_str(), eol_marker); + } + } + first = false; + + const int name_len = static_cast<int>(strlen(option->name())); + const int pad = name_len < uncrustify::limits::MAX_OPTION_NAME_LEN + ? (uncrustify::limits::MAX_OPTION_NAME_LEN - name_len) + : 1; + + fprintf(pfile, "%s%*.s= ", option->name(), pad, " "); + + if (option->type() == OT_STRING) + { + fprintf(pfile, "\"%s\"", val.c_str()); + } + else + { + fprintf(pfile, "%s", val.c_str()); + } + + if (with_doc) + { + const int val_len = static_cast<int>(val.length()); + fprintf(pfile, "%*.s # ", 8 - val_len, " "); + + for (auto pv = option->possibleValues(); *pv; ++pv) + { + fprintf(pfile, "%s%s", *pv, pv[1] ? "/" : ""); + } + } + fputs(eol_marker, pfile); + } + } + + if (with_doc) + { + fprintf(pfile, "%s", DOC_TEXT_END); + } + print_custom_keywords(pfile); // Print custom keywords + print_extensions(pfile); // Print custom file extensions + + fprintf(pfile, "# option(s) with 'not default' value: %d%s#%s", + non_default_values, eol_marker, eol_marker); +} // save_option_file + +} // namespace uncrustify |