/* * Copyright 2006 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "rtc_base/flags.h" #include #include #include #include "rtc_base/checks.h" #include "rtc_base/stringutils.h" #if defined(WEBRTC_WIN) // clang-format off // clang formating would change include order. #include #include // must come after windows.h // clang-format on #endif namespace { bool FlagEq(const char* arg, const char* flag) { // Compare two flags for equality. // 'arg' is the name of a flag passed via the command line and 'flag' is the // name of a flag defined with the DEFINE_* macros. // We compare the flags for equality, considering hyphens (-) and // underscores (_) to be equivalent, so that --flag-name and --flag_name both // match with --flag_name. while (*arg != '\0' && (*arg == *flag || (*arg == '-' && *flag == '_'))) { ++arg; ++flag; } return *arg == '\0' && *flag == '\0'; } } // namespace namespace rtc { // ----------------------------------------------------------------------------- // Implementation of Flag Flag::Flag(const char* file, const char* name, const char* comment, Type type, void* variable, FlagValue default__) : file_(file), name_(name), comment_(comment), type_(type), variable_(reinterpret_cast(variable)), default_(default__) { FlagList::Register(this); } void Flag::SetToDefault() { // Note that we cannot simply do '*variable_ = default_;' since // flag variables are not really of type FlagValue and thus may // be smaller! The FlagValue union is simply 'overlayed' on top // of a flag variable for convenient access. Since union members // are guarantee to be aligned at the beginning, this works. switch (type_) { case Flag::BOOL: variable_->b = default_.b; return; case Flag::INT: variable_->i = default_.i; return; case Flag::FLOAT: variable_->f = default_.f; return; case Flag::STRING: variable_->s = default_.s; return; } FATAL() << "unreachable code"; } static const char* Type2String(Flag::Type type) { switch (type) { case Flag::BOOL: return "bool"; case Flag::INT: return "int"; case Flag::FLOAT: return "float"; case Flag::STRING: return "string"; } FATAL() << "unreachable code"; } static void PrintFlagValue(Flag::Type type, FlagValue* p) { switch (type) { case Flag::BOOL: printf("%s", (p->b ? "true" : "false")); return; case Flag::INT: printf("%d", p->i); return; case Flag::FLOAT: printf("%f", p->f); return; case Flag::STRING: printf("%s", p->s); return; } FATAL() << "unreachable code"; } void Flag::Print(bool print_current_value) { printf(" --%s (%s) type: %s default: ", name_, comment_, Type2String(type_)); PrintFlagValue(type_, &default_); if (print_current_value) { printf(" current value: "); PrintFlagValue(type_, variable_); } printf("\n"); } // ----------------------------------------------------------------------------- // Implementation of FlagList Flag* FlagList::list_ = nullptr; FlagList::FlagList() { list_ = nullptr; } void FlagList::Print(const char* file, bool print_current_value) { // Since flag registration is likely by file (= C++ file), // we don't need to sort by file and still get grouped output. const char* current = nullptr; for (Flag* f = list_; f != nullptr; f = f->next()) { if (file == nullptr || file == f->file()) { if (current != f->file()) { printf("Flags from %s:\n", f->file()); current = f->file(); } f->Print(print_current_value); } } } Flag* FlagList::Lookup(const char* name) { Flag* f = list_; while (f != nullptr && !FlagEq(name, f->name())) f = f->next(); return f; } void FlagList::SplitArgument(const char* arg, char* buffer, int buffer_size, const char** name, const char** value, bool* is_bool) { *name = nullptr; *value = nullptr; *is_bool = false; if (*arg == '-') { // find the begin of the flag name arg++; // remove 1st '-' if (*arg == '-') arg++; // remove 2nd '-' if (arg[0] == 'n' && arg[1] == 'o' && Lookup(arg + 2)) { arg += 2; // remove "no" *is_bool = true; } *name = arg; // find the end of the flag name while (*arg != '\0' && *arg != '=') arg++; // get the value if any if (*arg == '=') { // make a copy so we can NUL-terminate flag name int n = static_cast(arg - *name); RTC_CHECK_LT(n, buffer_size); memcpy(buffer, *name, n * sizeof(char)); buffer[n] = '\0'; *name = buffer; // get the value *value = arg + 1; } } } int FlagList::SetFlagsFromCommandLine(int* argc, const char** argv, bool remove_flags) { // parse arguments for (int i = 1; i < *argc; /* see below */) { int j = i; // j > 0 const char* arg = argv[i++]; // split arg into flag components char buffer[1024]; const char* name; const char* value; bool is_bool; SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool); if (name != nullptr) { // lookup the flag Flag* flag = Lookup(name); if (flag == nullptr) { fprintf(stderr, "Error: unrecognized flag %s\n", arg); return j; } // if we still need a flag value, use the next argument if available if (flag->type() != Flag::BOOL && value == nullptr) { if (i < *argc) { value = argv[i++]; } else { fprintf(stderr, "Error: missing value for flag %s of type %s\n", arg, Type2String(flag->type())); return j; } } // set the flag char empty[] = {'\0'}; char* endp = empty; switch (flag->type()) { case Flag::BOOL: *flag->bool_variable() = !is_bool; break; case Flag::INT: *flag->int_variable() = strtol(value, &endp, 10); break; case Flag::FLOAT: *flag->float_variable() = strtod(value, &endp); break; case Flag::STRING: *flag->string_variable() = value; break; } // handle errors if ((flag->type() == Flag::BOOL && value != nullptr) || (flag->type() != Flag::BOOL && is_bool) || *endp != '\0') { fprintf(stderr, "Error: illegal value for flag %s of type %s\n", arg, Type2String(flag->type())); return j; } // remove the flag & value from the command if (remove_flags) while (j < i) argv[j++] = nullptr; } } // shrink the argument list if (remove_flags) { int j = 1; for (int i = 1; i < *argc; i++) { if (argv[i] != nullptr) argv[j++] = argv[i]; } *argc = j; } // parsed all flags successfully return 0; } void FlagList::Register(Flag* flag) { RTC_DCHECK(flag); RTC_DCHECK_GT(strlen(flag->name()), 0); // NOTE: Don't call Lookup() within Register because it accesses the name_ // of other flags in list_, and if the flags are coming from two different // compilation units, the initialization order between them is undefined, and // this will trigger an asan initialization-order-fiasco error. flag->next_ = list_; list_ = flag; } #if defined(WEBRTC_WIN) WindowsCommandLineArguments::WindowsCommandLineArguments() { // start by getting the command line. LPTSTR command_line = ::GetCommandLine(); // now, convert it to a list of wide char strings. LPWSTR* wide_argv = ::CommandLineToArgvW(command_line, &argc_); // now allocate an array big enough to hold that many string pointers. argv_ = new char*[argc_]; // iterate over the returned wide strings; for (int i = 0; i < argc_; ++i) { std::string s = rtc::ToUtf8(wide_argv[i], wcslen(wide_argv[i])); char* buffer = new char[s.length() + 1]; rtc::strcpyn(buffer, s.length() + 1, s.c_str()); // make sure the argv array has the right string at this point. argv_[i] = buffer; } LocalFree(wide_argv); } WindowsCommandLineArguments::~WindowsCommandLineArguments() { // need to free each string in the array, and then the array. for (int i = 0; i < argc_; i++) { delete[] argv_[i]; } delete[] argv_; } #endif // WEBRTC_WIN } // namespace rtc