/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2019, Google Inc. * * options.cpp - cam - Options parsing */ #include #include #include #include #include #include "options.h" /* ----------------------------------------------------------------------------- * Option */ const char *Option::typeName() const { switch (type) { case OptionNone: return "none"; case OptionInteger: return "integer"; case OptionString: return "string"; case OptionKeyValue: return "key=value"; } return "unknown"; } /* ----------------------------------------------------------------------------- * OptionBase */ template bool OptionsBase::empty() const { return values_.empty(); } template bool OptionsBase::valid() const { return valid_; } template bool OptionsBase::isSet(const T &opt) const { return values_.find(opt) != values_.end(); } template const OptionValue &OptionsBase::operator[](const T &opt) const { static const OptionValue empty; auto it = values_.find(opt); if (it != values_.end()) return it->second; return empty; } template void OptionsBase::invalidate() { valid_ = false; } template bool OptionsBase::parseValue(const T &opt, const Option &option, const char *arg) { OptionValue value; switch (option.type) { case OptionNone: break; case OptionInteger: unsigned int integer; if (arg) { char *endptr; integer = strtoul(arg, &endptr, 0); if (*endptr != '\0') return false; } else { integer = 0; } value = OptionValue(integer); break; case OptionString: value = OptionValue(arg ? arg : ""); break; case OptionKeyValue: KeyValueParser *kvParser = option.keyValueParser; KeyValueParser::Options keyValues = kvParser->parse(arg); if (!keyValues.valid()) return false; value = OptionValue(keyValues); break; } if (option.isArray) values_[opt].addValue(value); else values_[opt] = value; return true; } template class OptionsBase; template class OptionsBase; /* ----------------------------------------------------------------------------- * KeyValueParser */ bool KeyValueParser::addOption(const char *name, OptionType type, const char *help, OptionArgument argument) { if (!name) return false; if (!help || help[0] == '\0') return false; if (argument != ArgumentNone && type == OptionNone) return false; /* Reject duplicate options. */ if (optionsMap_.find(name) != optionsMap_.end()) return false; optionsMap_[name] = Option({ 0, type, name, argument, nullptr, help, nullptr, false }); return true; } KeyValueParser::Options KeyValueParser::parse(const char *arguments) { Options options; for (const char *pair = arguments; *arguments != '\0'; pair = arguments) { const char *comma = strchrnul(arguments, ','); size_t len = comma - pair; /* Skip over the comma. */ arguments = *comma == ',' ? comma + 1 : comma; /* Skip to the next pair if the pair is empty. */ if (!len) continue; std::string key; std::string value; const char *separator = static_cast(memchr(pair, '=', len)); if (!separator) { key = std::string(pair, len); value = ""; } else { key = std::string(pair, separator - pair); value = std::string(separator + 1, comma - separator - 1); } /* The key is mandatory, the value might be optional. */ if (key.empty()) continue; if (optionsMap_.find(key) == optionsMap_.end()) { std::cerr << "Invalid option " << key << std::endl; return options; } OptionArgument arg = optionsMap_[key].argument; if (value.empty() && arg == ArgumentRequired) { std::cerr << "Option " << key << " requires an argument" << std::endl; return options; } else if (!value.empty() && arg == ArgumentNone) { std::cerr << "Option " << key << " takes no argument" << std::endl; return options; } const Option &option = optionsMap_[key]; if (!options.parseValue(key, option, value.c_str())) { std::cerr << "Failed to parse '" << value << "' as " << option.typeName() << " for option " << key << std::endl; return options; } } options.valid_ = true; return options; } void KeyValueParser::usage(int indent) { unsigned int space = 0; for (auto const &iter : optionsMap_) { const Option &option = iter.second; unsigned int length = 14; if (option.argument != ArgumentNone) length += 1 + strlen(option.typeName()); if (option.argument == ArgumentOptional) length += 2; if (length > space) space = length; } space = (space + 7) / 8 * 8; for (auto const &iter : optionsMap_) { const Option &option = iter.second; std::string argument = option.name; if (option.argument != ArgumentNone) { if (option.argument == ArgumentOptional) argument += "[="; else argument += "="; argument += option.typeName(); if (option.argument == ArgumentOptional) argument += "]"; } std::cerr << std::setw(indent) << std::right << " " << std::setw(space) << std::left << argument; for (const char *help = option.help, *end = help; end;) { end = strchr(help, '\n'); if (end) { std::cerr << std::string(help, end - help + 1); std::cerr << std::setw(indent + space) << " "; help = end + 1; } else { std::cerr << help << std::endl; } } } } /* ----------------------------------------------------------------------------- * OptionValue */ OptionValue::OptionValue() : type_(ValueNone), integer_(0) { } OptionValue::OptionValue(int value) : type_(ValueInteger), integer_(value) { } OptionValue::OptionValue(const char *value) : type_(ValueString), integer_(0), string_(value) { } OptionValue::OptionValue(const std::string &value) : type_(ValueString), integer_(0), string_(value) { } OptionValue::OptionValue(const KeyValueParser::Options &value) : type_(ValueKeyValue), integer_(0), keyValues_(value) { } void OptionValue::addValue(const OptionValue &value) { assert(type_ == ValueNone || type_ == ValueArray); type_ = ValueArray; array_.push_back(value); } OptionValue::operator int() const { return toInteger(); } OptionValue::operator std::string() const { return toString(); } OptionValue::operator KeyValueParser::Options() const { return toKeyValues(); } OptionValue::operator std::vector() const { return toArray(); } int OptionValue::toInteger() const { if (type_ != ValueInteger) return 0; return integer_; } std::string OptionValue::toString() const { if (type_ != ValueString) return std::string(); return string_; } /* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2019, Google Inc. * * serialization_test.cpp - Base class for serialization tests */ #include "serialization_test.h" #include <algorithm> #include <iostream> #include <map> #include <libcamera/camera.h> #include <libcamera/camera_manager.h> #include <libcamera/controls.h> #include "test.h" using namespace std; using namespace libcamera; bool SerializationTest::equals(const ControlInfoMap &lhs, const ControlInfoMap &rhs) { std::map<unsigned int, ControlRange> rlhs; std::transform(lhs.begin(), lhs.end(), std::inserter(rlhs, rlhs.end()), [](const ControlInfoMap::value_type &v) -> decltype(rlhs)::value_type { return { v.first->id(), v.second }; }); std::map<unsigned int, ControlRange> rrhs; std::transform(rhs.begin(), rhs.end(), std::inserter(rrhs, rrhs.end()), [](const ControlInfoMap::value_type &v) -> decltype(rrhs)::value_type { return { v.first->id(), v.second }; }); if (rlhs == rrhs) return true; cerr << "lhs:" << endl; for (const auto &value : rlhs) cerr << "- " << value.first << ": " << value.second.toString() << endl; cerr << "rhs:" << endl; for (const auto &value : rrhs) cerr << "- " << value.first << ": " << value.second.toString() << endl; return false; } bool SerializationTest::equals(const ControlList &lhs, const ControlList &rhs) { std::map<unsigned int, ControlValue> rlhs; std::transform(lhs.begin(), lhs.end(), std::inserter(rlhs, rlhs.end()), [](const std::pair<unsigned int, ControlValue> &v) -> decltype(rlhs)::value_type { return { v.first, v.second }; }); std::map<unsigned int, ControlValue> rrhs; std::transform(rhs.begin(), rhs.end(), std::inserter(rrhs, rrhs.end()), [](const std::pair<unsigned int, ControlValue> &v) -> decltype(rrhs)::value_type { return { v.first, v.second }; }); if (rlhs == rrhs) return true; cerr << "lhs:" << endl; for (const auto &value : rlhs) cerr << "- " << value.first << ": " << value.second.toString() << endl; cerr << "rhs:" << endl; for (const auto &value : rrhs) cerr << "- " << value.first << ": " << value.second.toString() << endl; return false; }