#ifndef SQUID_CONFIGOPTION_H
#define SQUID_CONFIGOPTION_H
+#include <iosfwd>
#include <vector>
class StoreEntry;
+class ConfigParser;
+
+namespace Configuration {
+
+/// Interface for basic/low-level manipulation of a squid.conf directive value.
+/// Hides T's declarations from squid.conf parsing/reconfiguring/reporting code.
+///
+/// Implementations/specializations must not modify the current configuration
+/// (i.e. the Config objects and similar/related global state). To facilitate
+/// reuse, implementations/specializations should also be independent from any
+/// specific configuration directive name and its squid.conf location.
+///
+/// TODO: Support multi-directive components of various kinds.
+template <class T>
+class Component
+{
+public:
+ /* the code adding "TYPE: T" to cf.data.pre must specialize these */
+
+ /// creates a new T instance using the given parser; never returns nil
+ static T Parse(ConfigParser &);
-/* cache option parsers */
+ /// reports the current T instance configuration in squid.conf format
+ static void Print(std::ostream &, const T &);
+
+ /// destroys Parse() result
+ static void Free(T);
+};
+
+} // namespace Configuration
+
+/*
+ * Deprecated squid.conf option wrappers used by cache_dir handling code. These
+ * classes are similar to Configuration::Component<T>, but they merge T with T
+ * parsing API, making them ill-suited for handling SquidConfig data members
+ * with built-in C++ types and, more importantly, forcing SquidConfig users to
+ * know about parsing/dumping/freeing capabilities of each SquidConfig
+ * component. They also do not hide T details from the generic squid.conf
+ * parsing code -- one has to provide a type-specific parse_T() for each T.
+ */
class ConfigOption
{
return quotedStr.termedBuf();
}
+void
+ConfigParser::rejectDuplicateDirective()
+{
+ assert(cfg_directive);
+ throw TextException("duplicate configuration directive", Here());
+}
+
+void
+ConfigParser::closeDirective()
+{
+ assert(cfg_directive);
+ if (const auto garbage = PeekAtToken())
+ throw TextException(ToSBuf("trailing garbage at the end of a configuration directive: ", garbage), Here());
+ // TODO: cfg_directive = nullptr; // currently in generated code
+}
+
bool
ConfigParser::CfgFile::startParse(char *path)
{
enum TokenType {SimpleToken, QuotedToken, FunctionParameters};
void destruct();
+
+ /// stops parsing the current configuration directive
+ void closeDirective();
+
+ /// rejects configuration due to a repeated directive
+ void rejectDuplicateDirective();
+
static void ParseUShort(unsigned short *var);
static void ParseBool(bool *var);
static const char *QuoteString(const String &var);
#include "base/RunnersRegistry.h"
#include "cache_cf.h"
#include "CachePeer.h"
+#include "ConfigOption.h"
#include "ConfigParser.h"
#include "CpuAffinityMap.h"
#include "DiskIO/DiskIOModule.h"
}
}
+/*
+ * The templated functions below are essentially ConfigParser methods. They are
+ * not implemented as such because our generated code calling them is the only
+ * code that can instantiate implementations for each T -- we cannot place these
+ * definitions into ConfigParser.cc unless cf_parser.cci is moved there.
+ */
+
+// TODO: When adding Ts incompatible with this trivial API and implementation,
+// replace both with a ConfigParser-maintained table of seen directives.
+/// whether we have seen (and, hence, configured) the given directive
+template <typename T>
+static bool
+SawDirective(const T &raw)
+{
+ return bool(raw);
+}
+
+/// Sets the given raw SquidConfig data member.
+/// Extracts and interprets parser's configuration tokens.
+template <typename T>
+static void
+ParseDirective(T &raw, ConfigParser &parser)
+{
+ if (SawDirective(raw))
+ parser.rejectDuplicateDirective();
+
+ // TODO: parser.openDirective(directiveName);
+ Must(!raw);
+ raw = Configuration::Component<T>::Parse(parser);
+ Must(raw);
+ parser.closeDirective();
+}
+
+/// reports raw SquidConfig data member configuration using squid.conf syntax
+/// \param name the name of the configuration directive being dumped
+template <typename T>
+static void
+DumpDirective(const T &raw, StoreEntry *entry, const char *name)
+{
+ if (!SawDirective(raw))
+ return; // not configured
+
+ entry->append(name, strlen(name));
+ SBufStream os;
+ Configuration::Component<T>::Print(os, raw);
+ const auto buf = os.buf();
+ if (buf.length()) {
+ entry->append(" ", 1);
+ entry->append(buf.rawContent(), buf.length());
+ }
+ entry->append("\n", 1);
+}
+
+/// frees any resources associated with the given raw SquidConfig data member
+template <typename T>
+static void
+FreeDirective(T &raw)
+{
+ Configuration::Component<T>::Free(raw);
+
+ // While the implementation may change, there is no way to avoid zeroing.
+ // Even migration to a proper SquidConfig class would not help: While
+ // ordinary destructors do not need to zero data members, a SquidConfig
+ // destructor would have to zero to protect any SquidConfig::x destruction
+ // code from accidentally dereferencing an already destroyed Config.y.
+ static_assert(std::is_trivial<T>::value, "SquidConfig member is trivial");
+ memset(&raw, 0, sizeof(raw));
+}
+
static void
configDoConfigure(void)
{
fout << " parse_obsolete(token);";
} else if (!loc.size() || loc.compare("none") == 0) {
fout << "parse_" << type << "();";
+ } else if (type.find("::") != std::string::npos) {
+ fout << "ParseDirective<" << type << ">(" << loc << ", LegacyParser);";
} else {
fout << "parse_" << type << "(&" << loc << (array_flag ? "[0]" : "") << ");";
}
if (e.ifdef.size())
fout << "#if " << e.ifdef << std::endl;
- fout << " dump_" << e.type << "(entry, \"" << e.name << "\", " << e.loc << ");" << std::endl;
+ if (e.type.find("::") != std::string::npos)
+ fout << " DumpDirective<" << e.type << ">(" << e.loc << ", entry, \"" << e.name << "\");\n";
+ else
+ fout << " dump_" << e.type << "(entry, \"" << e.name << "\", " << e.loc << ");" << std::endl;
if (e.ifdef.size())
fout << "#endif" << std::endl;
if (e.ifdef.size())
fout << "#if " << e.ifdef << std::endl;
- fout << " free_" << e.type << "(&" << e.loc << (e.array_flag ? "[0]" : "") << ");" << std::endl;
+ if (e.type.find("::") != std::string::npos)
+ fout << " FreeDirective<" << e.type << ">(" << e.loc << ");\n";
+ else
+ fout << " free_" << e.type << "(&" << e.loc << (e.array_flag ? "[0]" : "") << ");" << std::endl;
if (e.ifdef.size())
fout << "#endif" << std::endl;