blob: 62d8827ac0272821ea3a08a3f209a568197e068f [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <getopt.h>
#include <lib/boot-options/boot-options.h>
#include <cstdio>
#include <cstdlib>
#include <type_traits>
#include <variant>
#include <rapidjson/document.h>
#include <rapidjson/filewritestream.h>
#include <rapidjson/prettywriter.h>
namespace {
#include "enum.h"
constexpr char kOptString[] = "j:ds:t::";
constexpr option kOptions[] = {
{"defaults", optional_argument, nullptr, 'd'},
{"json", required_argument, nullptr, 'j'},
{"set", required_argument, nullptr, 's'},
{"show", optional_argument, nullptr, 't'},
{},
};
void usage(const char* progname) {
fprintf(stderr, R"""(
Usage: %s OPTIONS...
--defaults, -d display all default values
--json=FILE, -j FILE write JSON description to FILE
--set=CMDLINE, -s CMDLINE set values from CMDLINE
--show[=KEY], -t[KEY] display KEY=VALUE (or all keys)
Each option is processed in turn. Thus earlier --set options affect the output
of later --show or --json options.
)""",
progname);
exit(EXIT_FAILURE);
}
template <typename T>
void WriteJsonValue(rapidjson::PrettyWriter<rapidjson::FileWriteStream>& writer, const T& value) {
if constexpr (std::is_same_v<T, bool>) {
writer.Bool(value);
} else if constexpr (std::is_same_v<T, uint64_t>) {
writer.Uint64(value);
} else if constexpr (std::is_same_v<T, uint32_t>) {
writer.Uint(value);
} else {
char buffer[128];
// fmemopen is supposed to write a NUL terminator, but if no output at all
// is sent before fclose, it might fail to.
buffer[0] = '\0';
FILE* f = fmemopen(buffer, sizeof(buffer), "w");
BootOptions::PrintValue(value, f);
fclose(f);
writer.String(buffer);
}
}
struct Equal {
template <typename EqT>
constexpr bool operator()(const EqT& value, const EqT& init) const {
return value == init;
}
template <typename... V>
constexpr bool operator()(const std::variant<V...>& value, const std::variant<V...>& init) const {
constexpr auto equal = [](const auto& value, const auto& init) {
if constexpr (std::is_same_v<decltype(value), decltype(init)>) {
return value == init;
}
return false;
};
return std::visit(equal, value, init);
}
};
template <typename T>
void WriteJsonOption(rapidjson::PrettyWriter<rapidjson::FileWriteStream>& writer, const char* name,
const char* type, const char* member, std::string_view doc, const T& init,
const T& value) {
writer.StartObject();
writer.Key("name");
writer.String(name);
writer.Key("type");
if constexpr (std::is_enum_v<T>) {
writer.StartArray();
Enum<T>(EnumEnumerator{[&writer](std::string_view name) {
writer.String(name.data(),
static_cast<rapidjson::SizeType>(name.size()));
},
T{}});
writer.EndArray();
} else {
writer.String(type);
}
// options.inc uses R"""(...)""" with line breaks at the start and end.
ZX_ASSERT(doc.substr(0, 1) == "\n");
doc.remove_prefix(1);
ZX_ASSERT(doc.substr(doc.size() - 1) == "\n");
doc.remove_suffix(1);
writer.Key("documentation");
writer.String(doc.data(), static_cast<rapidjson::SizeType>(doc.size()));
writer.Key("default");
WriteJsonValue<T>(writer, init);
if (!Equal{}(value, init)) {
writer.Key("value");
WriteJsonValue<T>(writer, value);
}
writer.EndObject();
}
void WriteJson(const BootOptions& options, const char* json_output) {
auto f = fopen(json_output, "w");
if (!f) {
perror(json_output);
exit(1);
}
char buffer[BUFSIZ];
rapidjson::FileWriteStream os(f, buffer, sizeof(buffer));
rapidjson::PrettyWriter<rapidjson::FileWriteStream> writer(os);
writer.StartObject();
#define DEFINE_OPTION(name, type, member, init, doc) \
WriteJsonOption<type>(writer, name, #type, #member, doc, type init, options.member);
writer.Key("common");
writer.StartArray();
#include <lib/boot-options/options.inc>
writer.EndArray();
writer.Key("x86");
writer.StartArray();
#include <lib/boot-options/x86.inc>
writer.EndArray();
writer.Key("arm64");
writer.StartArray();
#include <lib/boot-options/arm64.inc>
writer.EndArray();
#undef DEFINE_OPTION
writer.EndObject();
fclose(f);
}
} // namespace
int main(int argc, char** argv) {
BootOptions options;
bool nop = true;
int opt;
while ((opt = getopt_long(argc, argv, kOptString, kOptions, nullptr)) != -1) {
switch (opt) {
case 'd':
BootOptions{}.Show(false);
break;
case 'j':
WriteJson(options, optarg);
break;
case 's':
options.SetMany(optarg, stderr);
break;
case 't':
if (optarg) {
options.Show(optarg, false);
} else {
options.Show(false);
}
break;
default:
usage(argv[0]);
}
nop = false;
}
if (argc != optind || nop) {
usage(argv[0]);
}
return 0;
}