|  | /* | 
|  | *  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 <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/stringutils.h" | 
|  |  | 
|  | #if defined(WEBRTC_WIN) | 
|  | #include <windows.h> | 
|  | #include <shellapi.h> | 
|  | #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<FlagValue*>(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<int>(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 |