summaryrefslogtreecommitdiffstats
path: root/debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/option.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/option.cpp')
-rw-r--r--debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/option.cpp1247
1 files changed, 1247 insertions, 0 deletions
diff --git a/debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/option.cpp b/debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/option.cpp
new file mode 100644
index 00000000..5089f37a
--- /dev/null
+++ b/debian/uncrustify-trinity/uncrustify-trinity-0.74.0/src/option.cpp
@@ -0,0 +1,1247 @@
+/**
+ * @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 "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(...) \
+ do { log_config(); LOG_FMT(LNOTE, __VA_ARGS__); \
+ } while (0)
+
+
+//-----------------------------------------------------------------------------
+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 backslashs 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
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+
+//BEGIN Option<T> and helpers
+
+
+//-----------------------------------------------------------------------------
+OptionWarning::OptionWarning(const char *filename, Severity severity)
+{
+ if (severity != MINOR)
+ {
+ ++cpd.error_count;
+ }
+
+ 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)
+{
+ if (severity != MINOR)
+ {
+ ++cpd.error_count;
+ }
+ 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
+ unc_text 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;
+ }
+ }
+ 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);
+ return(false);
+ }
+ // 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];
+
+ // 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);
+ return(false);
+ }
+ }
+
+ ++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_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