#include "config.h"
#include "gflags.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#if defined(HAVE_FNMATCH_H)
# include <fnmatch.h>
#elif defined(HAVE_SHLWAPI_H)
# include <shlwapi.h>
#endif
#ifdef _MSC_VER
# define strcasecmp _stricmp
#endif
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "mutex.h"
#include "util.h"
using namespace MUTEX_NAMESPACE;
DEFINE_string(flagfile, "", "load flags from file");
DEFINE_string(fromenv, "", "set flags from the environment"
" [use 'export FLAGS_flag1=value']");
DEFINE_string(tryfromenv, "", "set flags from the environment if present");
DEFINE_string(undefok, "", "comma-separated list of flag names that it is okay to specify "
"on the command line even if the program does not define a flag "
"with that name. IMPORTANT: flags in this list that have "
"arguments MUST use the flag=value format");
namespace GFLAGS_NAMESPACE {
using std::map;
using std::pair;
using std::sort;
using std::string;
using std::vector;
void GFLAGS_DLL_DECL (*gflags_exitfunc)(int) = &exit;
const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
namespace {
static const char kError[] = "ERROR: ";
static bool allow_command_line_reparsing = false;
static bool logging_is_probably_set_up = false;
typedef bool (*ValidateFnProto)();
enum DieWhenReporting { DIE, DO_NOT_DIE };
static void ReportError(DieWhenReporting should_die, const char* format, ...) {
char error_message[255];
va_list ap;
va_start(ap, format);
vsnprintf(error_message, sizeof(error_message), format, ap);
va_end(ap);
fprintf(stderr, "%s", error_message);
fflush(stderr); if (should_die == DIE) gflags_exitfunc(1);
}
class CommandLineFlag;
class FlagValue {
public:
FlagValue(void* valbuf, const char* type, bool transfer_ownership_of_value);
~FlagValue();
bool ParseFrom(const char* spec);
string ToString() const;
private:
friend class CommandLineFlag; friend class GFLAGS_NAMESPACE::FlagSaverImpl; friend class FlagRegistry; template <typename T> friend T GetFromEnv(const char*, const char*, T);
friend bool TryParseLocked(const CommandLineFlag*, FlagValue*,
const char*, string*);
enum ValueType {
FV_BOOL = 0,
FV_INT32 = 1,
FV_INT64 = 2,
FV_UINT64 = 3,
FV_DOUBLE = 4,
FV_STRING = 5,
FV_MAX_INDEX = 5,
};
const char* TypeName() const;
bool Equal(const FlagValue& x) const;
FlagValue* New() const; void CopyFrom(const FlagValue& x);
int ValueSize() const;
bool Validate(const char* flagname, ValidateFnProto validate_fn_proto) const;
void* value_buffer_; int8 type_; bool owns_value_;
FlagValue(const FlagValue&); void operator=(const FlagValue&);
};
#define VALUE_AS(type) *reinterpret_cast<type*>(value_buffer_)
#define OTHER_VALUE_AS(fv, type) *reinterpret_cast<type*>(fv.value_buffer_)
#define SET_VALUE_AS(type, value) VALUE_AS(type) = (value)
FlagValue::FlagValue(void* valbuf, const char* type,
bool transfer_ownership_of_value)
: value_buffer_(valbuf),
owns_value_(transfer_ownership_of_value) {
for (type_ = 0; type_ <= FV_MAX_INDEX; ++type_) {
if (!strcmp(type, TypeName())) {
break;
}
}
assert(type_ <= FV_MAX_INDEX); }
FlagValue::~FlagValue() {
if (!owns_value_) {
return;
}
switch (type_) {
case FV_BOOL: delete reinterpret_cast<bool*>(value_buffer_); break;
case FV_INT32: delete reinterpret_cast<int32*>(value_buffer_); break;
case FV_INT64: delete reinterpret_cast<int64*>(value_buffer_); break;
case FV_UINT64: delete reinterpret_cast<uint64*>(value_buffer_); break;
case FV_DOUBLE: delete reinterpret_cast<double*>(value_buffer_); break;
case FV_STRING: delete reinterpret_cast<string*>(value_buffer_); break;
}
}
bool FlagValue::ParseFrom(const char* value) {
if (type_ == FV_BOOL) {
const char* kTrue[] = { "1", "t", "true", "y", "yes" };
const char* kFalse[] = { "0", "f", "false", "n", "no" };
COMPILE_ASSERT(sizeof(kTrue) == sizeof(kFalse), true_false_equal);
for (size_t i = 0; i < sizeof(kTrue)/sizeof(*kTrue); ++i) {
if (strcasecmp(value, kTrue[i]) == 0) {
SET_VALUE_AS(bool, true);
return true;
} else if (strcasecmp(value, kFalse[i]) == 0) {
SET_VALUE_AS(bool, false);
return true;
}
}
return false;
} else if (type_ == FV_STRING) {
SET_VALUE_AS(string, value);
return true;
}
if (value[0] == '\0') return false;
char* end;
int base = 10; if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
base = 16;
errno = 0;
switch (type_) {
case FV_INT32: {
const int64 r = strto64(value, &end, base);
if (errno || end != value + strlen(value)) return false; if (static_cast<int32>(r) != r) return false;
SET_VALUE_AS(int32, static_cast<int32>(r));
return true;
}
case FV_INT64: {
const int64 r = strto64(value, &end, base);
if (errno || end != value + strlen(value)) return false; SET_VALUE_AS(int64, r);
return true;
}
case FV_UINT64: {
while (*value == ' ') value++;
if (*value == '-') return false; const uint64 r = strtou64(value, &end, base);
if (errno || end != value + strlen(value)) return false; SET_VALUE_AS(uint64, r);
return true;
}
case FV_DOUBLE: {
const double r = strtod(value, &end);
if (errno || end != value + strlen(value)) return false; SET_VALUE_AS(double, r);
return true;
}
default: {
assert(false); return false;
}
}
}
string FlagValue::ToString() const {
char intbuf[64]; switch (type_) {
case FV_BOOL:
return VALUE_AS(bool) ? "true" : "false";
case FV_INT32:
snprintf(intbuf, sizeof(intbuf), "%" PRId32, VALUE_AS(int32));
return intbuf;
case FV_INT64:
snprintf(intbuf, sizeof(intbuf), "%" PRId64, VALUE_AS(int64));
return intbuf;
case FV_UINT64:
snprintf(intbuf, sizeof(intbuf), "%" PRIu64, VALUE_AS(uint64));
return intbuf;
case FV_DOUBLE:
snprintf(intbuf, sizeof(intbuf), "%.17g", VALUE_AS(double));
return intbuf;
case FV_STRING:
return VALUE_AS(string);
default:
assert(false);
return ""; }
}
bool FlagValue::Validate(const char* flagname,
ValidateFnProto validate_fn_proto) const {
switch (type_) {
case FV_BOOL:
return reinterpret_cast<bool (*)(const char*, bool)>(
validate_fn_proto)(flagname, VALUE_AS(bool));
case FV_INT32:
return reinterpret_cast<bool (*)(const char*, int32)>(
validate_fn_proto)(flagname, VALUE_AS(int32));
case FV_INT64:
return reinterpret_cast<bool (*)(const char*, int64)>(
validate_fn_proto)(flagname, VALUE_AS(int64));
case FV_UINT64:
return reinterpret_cast<bool (*)(const char*, uint64)>(
validate_fn_proto)(flagname, VALUE_AS(uint64));
case FV_DOUBLE:
return reinterpret_cast<bool (*)(const char*, double)>(
validate_fn_proto)(flagname, VALUE_AS(double));
case FV_STRING:
return reinterpret_cast<bool (*)(const char*, const string&)>(
validate_fn_proto)(flagname, VALUE_AS(string));
default:
assert(false); return false;
}
}
const char* FlagValue::TypeName() const {
static const char types[] =
"bool\0xx"
"int32\0x"
"int64\0x"
"uint64\0"
"double\0"
"string";
if (type_ > FV_MAX_INDEX) {
assert(false);
return "";
}
return &types[type_ * 7];
}
bool FlagValue::Equal(const FlagValue& x) const {
if (type_ != x.type_)
return false;
switch (type_) {
case FV_BOOL: return VALUE_AS(bool) == OTHER_VALUE_AS(x, bool);
case FV_INT32: return VALUE_AS(int32) == OTHER_VALUE_AS(x, int32);
case FV_INT64: return VALUE_AS(int64) == OTHER_VALUE_AS(x, int64);
case FV_UINT64: return VALUE_AS(uint64) == OTHER_VALUE_AS(x, uint64);
case FV_DOUBLE: return VALUE_AS(double) == OTHER_VALUE_AS(x, double);
case FV_STRING: return VALUE_AS(string) == OTHER_VALUE_AS(x, string);
default: assert(false); return false; }
}
FlagValue* FlagValue::New() const {
const char *type = TypeName();
switch (type_) {
case FV_BOOL: return new FlagValue(new bool(false), type, true);
case FV_INT32: return new FlagValue(new int32(0), type, true);
case FV_INT64: return new FlagValue(new int64(0), type, true);
case FV_UINT64: return new FlagValue(new uint64(0), type, true);
case FV_DOUBLE: return new FlagValue(new double(0.0), type, true);
case FV_STRING: return new FlagValue(new string, type, true);
default: assert(false); return NULL; }
}
void FlagValue::CopyFrom(const FlagValue& x) {
assert(type_ == x.type_);
switch (type_) {
case FV_BOOL: SET_VALUE_AS(bool, OTHER_VALUE_AS(x, bool)); break;
case FV_INT32: SET_VALUE_AS(int32, OTHER_VALUE_AS(x, int32)); break;
case FV_INT64: SET_VALUE_AS(int64, OTHER_VALUE_AS(x, int64)); break;
case FV_UINT64: SET_VALUE_AS(uint64, OTHER_VALUE_AS(x, uint64)); break;
case FV_DOUBLE: SET_VALUE_AS(double, OTHER_VALUE_AS(x, double)); break;
case FV_STRING: SET_VALUE_AS(string, OTHER_VALUE_AS(x, string)); break;
default: assert(false); }
}
int FlagValue::ValueSize() const {
if (type_ > FV_MAX_INDEX) {
assert(false); return 0;
}
static const uint8 valuesize[] = {
sizeof(bool),
sizeof(int32),
sizeof(int64),
sizeof(uint64),
sizeof(double),
sizeof(string),
};
return valuesize[type_];
}
class CommandLineFlag {
public:
CommandLineFlag(const char* name, const char* help, const char* filename,
FlagValue* current_val, FlagValue* default_val);
~CommandLineFlag();
const char* name() const { return name_; }
const char* help() const { return help_; }
const char* filename() const { return file_; }
const char* CleanFileName() const; string current_value() const { return current_->ToString(); }
string default_value() const { return defvalue_->ToString(); }
const char* type_name() const { return defvalue_->TypeName(); }
ValidateFnProto validate_function() const { return validate_fn_proto_; }
const void* flag_ptr() const { return current_->value_buffer_; }
void FillCommandLineFlagInfo(struct CommandLineFlagInfo* result);
bool Validate(const FlagValue& value) const;
bool ValidateCurrent() const { return Validate(*current_); }
private:
friend class FlagRegistry;
friend class GFLAGS_NAMESPACE::FlagSaverImpl; friend bool AddFlagValidator(const void*, ValidateFnProto);
void CopyFrom(const CommandLineFlag& src);
void UpdateModifiedBit();
const char* const name_; const char* const help_; const char* const file_; bool modified_; FlagValue* defvalue_; FlagValue* current_; ValidateFnProto validate_fn_proto_;
CommandLineFlag(const CommandLineFlag&); void operator=(const CommandLineFlag&);
};
CommandLineFlag::CommandLineFlag(const char* name, const char* help,
const char* filename,
FlagValue* current_val, FlagValue* default_val)
: name_(name), help_(help), file_(filename), modified_(false),
defvalue_(default_val), current_(current_val), validate_fn_proto_(NULL) {
}
CommandLineFlag::~CommandLineFlag() {
delete current_;
delete defvalue_;
}
const char* CommandLineFlag::CleanFileName() const {
static const char kRootDir[] = "";
if (sizeof(kRootDir)-1 == 0) return filename();
const char* clean_name = filename() + strlen(filename()) - 1;
while ( clean_name > filename() ) {
if (*clean_name == PATH_SEPARATOR) {
if (strncmp(clean_name, kRootDir, sizeof(kRootDir)-1) == 0) {
clean_name += sizeof(kRootDir)-1; break;
}
}
--clean_name;
}
while ( *clean_name == PATH_SEPARATOR ) ++clean_name; return clean_name;
}
void CommandLineFlag::FillCommandLineFlagInfo(
CommandLineFlagInfo* result) {
result->name = name();
result->type = type_name();
result->description = help();
result->current_value = current_value();
result->default_value = default_value();
result->filename = CleanFileName();
UpdateModifiedBit();
result->is_default = !modified_;
result->has_validator_fn = validate_function() != NULL;
result->flag_ptr = flag_ptr();
}
void CommandLineFlag::UpdateModifiedBit() {
if (!modified_ && !current_->Equal(*defvalue_)) {
modified_ = true;
}
}
void CommandLineFlag::CopyFrom(const CommandLineFlag& src) {
if (modified_ != src.modified_) modified_ = src.modified_;
if (!current_->Equal(*src.current_)) current_->CopyFrom(*src.current_);
if (!defvalue_->Equal(*src.defvalue_)) defvalue_->CopyFrom(*src.defvalue_);
if (validate_fn_proto_ != src.validate_fn_proto_)
validate_fn_proto_ = src.validate_fn_proto_;
}
bool CommandLineFlag::Validate(const FlagValue& value) const {
if (validate_function() == NULL)
return true;
else
return value.Validate(name(), validate_function());
}
struct StringCmp { bool operator() (const char* s1, const char* s2) const {
return (strcmp(s1, s2) < 0);
}
};
class FlagRegistry {
public:
FlagRegistry() {
}
~FlagRegistry() {
for (FlagMap::iterator p = flags_.begin(), e = flags_.end(); p != e; ++p) {
CommandLineFlag* flag = p->second;
delete flag;
}
}
static void DeleteGlobalRegistry() {
delete global_registry_;
global_registry_ = NULL;
}
void RegisterFlag(CommandLineFlag* flag);
void Lock() { lock_.Lock(); }
void Unlock() { lock_.Unlock(); }
CommandLineFlag* FindFlagLocked(const char* name);
CommandLineFlag* FindFlagViaPtrLocked(const void* flag_ptr);
CommandLineFlag* SplitArgumentLocked(const char* argument,
string* key, const char** v,
string* error_message);
bool SetFlagLocked(CommandLineFlag* flag, const char* value,
FlagSettingMode set_mode, string* msg);
static FlagRegistry* GlobalRegistry();
private:
friend class GFLAGS_NAMESPACE::FlagSaverImpl; friend class CommandLineFlagParser; friend void GFLAGS_NAMESPACE::GetAllFlags(vector<CommandLineFlagInfo>*);
typedef map<const char*, CommandLineFlag*, StringCmp> FlagMap;
typedef FlagMap::iterator FlagIterator;
typedef FlagMap::const_iterator FlagConstIterator;
FlagMap flags_;
typedef map<const void*, CommandLineFlag*> FlagPtrMap;
FlagPtrMap flags_by_ptr_;
static FlagRegistry* global_registry_;
Mutex lock_;
static Mutex global_registry_lock_;
static void InitGlobalRegistry();
FlagRegistry(const FlagRegistry&);
FlagRegistry& operator=(const FlagRegistry&);
};
class FlagRegistryLock {
public:
explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); }
~FlagRegistryLock() { fr_->Unlock(); }
private:
FlagRegistry *const fr_;
};
void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
Lock();
pair<FlagIterator, bool> ins =
flags_.insert(pair<const char*, CommandLineFlag*>(flag->name(), flag));
if (ins.second == false) { if (strcmp(ins.first->second->filename(), flag->filename()) != 0) {
ReportError(DIE, "ERROR: flag '%s' was defined more than once "
"(in files '%s' and '%s').\n",
flag->name(),
ins.first->second->filename(),
flag->filename());
} else {
ReportError(DIE, "ERROR: something wrong with flag '%s' in file '%s'. "
"One possibility: file '%s' is being linked both statically "
"and dynamically into this executable.\n",
flag->name(),
flag->filename(), flag->filename());
}
}
flags_by_ptr_[flag->current_->value_buffer_] = flag;
Unlock();
}
CommandLineFlag* FlagRegistry::FindFlagLocked(const char* name) {
FlagConstIterator i = flags_.find(name);
if (i == flags_.end()) {
return NULL;
} else {
return i->second;
}
}
CommandLineFlag* FlagRegistry::FindFlagViaPtrLocked(const void* flag_ptr) {
FlagPtrMap::const_iterator i = flags_by_ptr_.find(flag_ptr);
if (i == flags_by_ptr_.end()) {
return NULL;
} else {
return i->second;
}
}
CommandLineFlag* FlagRegistry::SplitArgumentLocked(const char* arg,
string* key,
const char** v,
string* error_message) {
const char* flag_name;
const char* value = strchr(arg, '=');
if (value == NULL) {
key->assign(arg);
*v = NULL;
} else {
key->assign(arg, value-arg);
*v = ++value; }
flag_name = key->c_str();
CommandLineFlag* flag = FindFlagLocked(flag_name);
if (flag == NULL) {
if (!(flag_name[0] == 'n' && flag_name[1] == 'o')) {
*error_message = StringPrintf("%sunknown command line flag '%s'\n",
kError, key->c_str());
return NULL;
}
flag = FindFlagLocked(flag_name+2);
if (flag == NULL) {
*error_message = StringPrintf("%sunknown command line flag '%s'\n",
kError, key->c_str());
return NULL;
}
if (strcmp(flag->type_name(), "bool") != 0) {
*error_message = StringPrintf(
"%sboolean value (%s) specified for %s command line flag\n",
kError, key->c_str(), flag->type_name());
return NULL;
}
key->assign(flag_name+2); *v = "0";
}
if (*v == NULL && strcmp(flag->type_name(), "bool") == 0) {
*v = "1"; }
return flag;
}
bool TryParseLocked(const CommandLineFlag* flag, FlagValue* flag_value,
const char* value, string* msg) {
FlagValue* tentative_value = flag_value->New();
if (!tentative_value->ParseFrom(value)) {
if (msg) {
StringAppendF(msg,
"%sillegal value '%s' specified for %s flag '%s'\n",
kError, value,
flag->type_name(), flag->name());
}
delete tentative_value;
return false;
} else if (!flag->Validate(*tentative_value)) {
if (msg) {
StringAppendF(msg,
"%sfailed validation of new value '%s' for flag '%s'\n",
kError, tentative_value->ToString().c_str(),
flag->name());
}
delete tentative_value;
return false;
} else {
flag_value->CopyFrom(*tentative_value);
if (msg) {
StringAppendF(msg, "%s set to %s\n",
flag->name(), flag_value->ToString().c_str());
}
delete tentative_value;
return true;
}
}
bool FlagRegistry::SetFlagLocked(CommandLineFlag* flag,
const char* value,
FlagSettingMode set_mode,
string* msg) {
flag->UpdateModifiedBit();
switch (set_mode) {
case SET_FLAGS_VALUE: {
if (!TryParseLocked(flag, flag->current_, value, msg))
return false;
flag->modified_ = true;
break;
}
case SET_FLAG_IF_DEFAULT: {
if (!flag->modified_) {
if (!TryParseLocked(flag, flag->current_, value, msg))
return false;
flag->modified_ = true;
} else {
*msg = StringPrintf("%s set to %s",
flag->name(), flag->current_value().c_str());
}
break;
}
case SET_FLAGS_DEFAULT: {
if (!TryParseLocked(flag, flag->defvalue_, value, msg))
return false;
if (!flag->modified_) {
TryParseLocked(flag, flag->current_, value, NULL);
}
break;
}
default: {
assert(false);
return false;
}
}
return true;
}
FlagRegistry* FlagRegistry::global_registry_ = NULL;
Mutex FlagRegistry::global_registry_lock_(Mutex::LINKER_INITIALIZED);
FlagRegistry* FlagRegistry::GlobalRegistry() {
MutexLock acquire_lock(&global_registry_lock_);
if (!global_registry_) {
global_registry_ = new FlagRegistry;
}
return global_registry_;
}
class CommandLineFlagParser {
public:
explicit CommandLineFlagParser(FlagRegistry* reg) : registry_(reg) {}
~CommandLineFlagParser() {}
uint32 ParseNewCommandLineFlags(int* argc, char*** argv, bool remove_flags);
void ValidateAllFlags();
bool ReportErrors();
string ProcessSingleOptionLocked(CommandLineFlag* flag,
const char* value,
FlagSettingMode set_mode);
string ProcessOptionsFromStringLocked(const string& contentdata,
FlagSettingMode set_mode);
string ProcessFlagfileLocked(const string& flagval, FlagSettingMode set_mode);
string ProcessFromenvLocked(const string& flagval, FlagSettingMode set_mode,
bool errors_are_fatal);
private:
FlagRegistry* const registry_;
map<string, string> error_flags_; map<string, string> undefined_names_; };
static void ParseFlagList(const char* value, vector<string>* flags) {
for (const char *p = value; p && *p; value = p) {
p = strchr(value, ',');
size_t len;
if (p) {
len = p - value;
p++;
} else {
len = strlen(value);
}
if (len == 0)
ReportError(DIE, "ERROR: empty flaglist entry\n");
if (value[0] == '-')
ReportError(DIE, "ERROR: flag \"%*s\" begins with '-'\n", len, value);
flags->push_back(string(value, len));
}
}
#define PFATAL(s) do { perror(s); gflags_exitfunc(1); } while (0)
static string ReadFileIntoString(const char* filename) {
const int kBufSize = 8092;
char buffer[kBufSize];
string s;
FILE* fp;
if ((errno = SafeFOpen(&fp, filename, "r")) != 0) PFATAL(filename);
size_t n;
while ( (n=fread(buffer, 1, kBufSize, fp)) > 0 ) {
if (ferror(fp)) PFATAL(filename);
s.append(buffer, n);
}
fclose(fp);
return s;
}
uint32 CommandLineFlagParser::ParseNewCommandLineFlags(int* argc, char*** argv,
bool remove_flags) {
const char *program_name = strrchr((*argv)[0], PATH_SEPARATOR); program_name = (program_name == NULL ? (*argv)[0] : program_name+1);
int first_nonopt = *argc;
registry_->Lock();
for (int i = 1; i < first_nonopt; i++) {
char* arg = (*argv)[i];
if (arg[0] != '-' || (arg[0] == '-' && arg[1] == '\0')) { memmove((*argv) + i, (*argv) + i+1, (*argc - (i+1)) * sizeof((*argv)[i]));
(*argv)[*argc-1] = arg; first_nonopt--; i--; continue;
}
if (arg[0] == '-') arg++; if (arg[0] == '-') arg++;
if (*arg == '\0') {
first_nonopt = i+1;
break;
}
string key;
const char* value;
string error_message;
CommandLineFlag* flag = registry_->SplitArgumentLocked(arg, &key, &value,
&error_message);
if (flag == NULL) {
undefined_names_[key] = ""; error_flags_[key] = error_message;
continue;
}
if (value == NULL) {
assert(strcmp(flag->type_name(), "bool") != 0);
if (i+1 >= first_nonopt) {
error_flags_[key] = (string(kError) + "flag '" + (*argv)[i] + "'"
+ " is missing its argument");
if (flag->help() && flag->help()[0] > '\001') {
error_flags_[key] += string("; flag description: ") + flag->help();
}
error_flags_[key] += "\n";
break; } else {
value = (*argv)[++i];
if (value[0] == '-'
&& strcmp(flag->type_name(), "string") == 0
&& (strstr(flag->help(), "true")
|| strstr(flag->help(), "false"))) {
LOG(WARNING) << "Did you really mean to set flag '"
<< flag->name() << "' to the value '"
<< value << "'?";
}
}
}
ProcessSingleOptionLocked(flag, value, SET_FLAGS_VALUE);
}
registry_->Unlock();
if (remove_flags) { (*argv)[first_nonopt-1] = (*argv)[0];
(*argv) += (first_nonopt-1);
(*argc) -= (first_nonopt-1);
first_nonopt = 1; }
logging_is_probably_set_up = true;
return first_nonopt;
}
string CommandLineFlagParser::ProcessFlagfileLocked(const string& flagval,
FlagSettingMode set_mode) {
if (flagval.empty())
return "";
string msg;
vector<string> filename_list;
ParseFlagList(flagval.c_str(), &filename_list); for (size_t i = 0; i < filename_list.size(); ++i) {
const char* file = filename_list[i].c_str();
msg += ProcessOptionsFromStringLocked(ReadFileIntoString(file), set_mode);
}
return msg;
}
string CommandLineFlagParser::ProcessFromenvLocked(const string& flagval,
FlagSettingMode set_mode,
bool errors_are_fatal) {
if (flagval.empty())
return "";
string msg;
vector<string> flaglist;
ParseFlagList(flagval.c_str(), &flaglist);
for (size_t i = 0; i < flaglist.size(); ++i) {
const char* flagname = flaglist[i].c_str();
CommandLineFlag* flag = registry_->FindFlagLocked(flagname);
if (flag == NULL) {
error_flags_[flagname] =
StringPrintf("%sunknown command line flag '%s' "
"(via --fromenv or --tryfromenv)\n",
kError, flagname);
undefined_names_[flagname] = "";
continue;
}
const string envname = string("FLAGS_") + string(flagname);
string envval;
if (!SafeGetEnv(envname.c_str(), envval)) {
if (errors_are_fatal) {
error_flags_[flagname] = (string(kError) + envname +
" not found in environment\n");
}
continue;
}
if (envval == "fromenv" || envval == "tryfromenv") {
error_flags_[flagname] =
StringPrintf("%sinfinite recursion on environment flag '%s'\n",
kError, envval.c_str());
continue;
}
msg += ProcessSingleOptionLocked(flag, envval.c_str(), set_mode);
}
return msg;
}
string CommandLineFlagParser::ProcessSingleOptionLocked(
CommandLineFlag* flag, const char* value, FlagSettingMode set_mode) {
string msg;
if (value && !registry_->SetFlagLocked(flag, value, set_mode, &msg)) {
error_flags_[flag->name()] = msg;
return "";
}
if (strcmp(flag->name(), "flagfile") == 0) {
msg += ProcessFlagfileLocked(FLAGS_flagfile, set_mode);
} else if (strcmp(flag->name(), "fromenv") == 0) {
msg += ProcessFromenvLocked(FLAGS_fromenv, set_mode, true);
} else if (strcmp(flag->name(), "tryfromenv") == 0) {
msg += ProcessFromenvLocked(FLAGS_tryfromenv, set_mode, false);
}
return msg;
}
void CommandLineFlagParser::ValidateAllFlags() {
FlagRegistryLock frl(registry_);
for (FlagRegistry::FlagConstIterator i = registry_->flags_.begin();
i != registry_->flags_.end(); ++i) {
if (!i->second->ValidateCurrent()) {
if (error_flags_[i->second->name()].empty())
error_flags_[i->second->name()] =
string(kError) + "--" + i->second->name() +
" must be set on the commandline"
" (default value fails validation)\n";
}
}
}
bool CommandLineFlagParser::ReportErrors() {
if (!FLAGS_undefok.empty()) {
vector<string> flaglist;
ParseFlagList(FLAGS_undefok.c_str(), &flaglist);
for (size_t i = 0; i < flaglist.size(); ++i) {
const string no_version = string("no") + flaglist[i];
if (undefined_names_.find(flaglist[i]) != undefined_names_.end()) {
error_flags_[flaglist[i]] = ""; } else if (undefined_names_.find(no_version) != undefined_names_.end()) {
error_flags_[no_version] = "";
}
}
}
if (allow_command_line_reparsing) {
for (map<string, string>::const_iterator it = undefined_names_.begin();
it != undefined_names_.end(); ++it)
error_flags_[it->first] = ""; }
bool found_error = false;
string error_message;
for (map<string, string>::const_iterator it = error_flags_.begin();
it != error_flags_.end(); ++it) {
if (!it->second.empty()) {
error_message.append(it->second.data(), it->second.size());
found_error = true;
}
}
if (found_error)
ReportError(DO_NOT_DIE, "%s", error_message.c_str());
return found_error;
}
string CommandLineFlagParser::ProcessOptionsFromStringLocked(
const string& contentdata, FlagSettingMode set_mode) {
string retval;
const char* flagfile_contents = contentdata.c_str();
bool flags_are_relevant = true; bool in_filename_section = false;
const char* line_end = flagfile_contents;
for (; line_end; flagfile_contents = line_end + 1) {
while (*flagfile_contents && isspace(*flagfile_contents))
++flagfile_contents;
line_end = strchr(flagfile_contents, '\n');
size_t len = line_end ? line_end - flagfile_contents
: strlen(flagfile_contents);
string line(flagfile_contents, len);
if (line.empty() || line[0] == '#') {
} else if (line[0] == '-') { in_filename_section = false; if (!flags_are_relevant) continue;
const char* name_and_val = line.c_str() + 1; if (*name_and_val == '-')
name_and_val++; string key;
const char* value;
string error_message;
CommandLineFlag* flag = registry_->SplitArgumentLocked(name_and_val,
&key, &value,
&error_message);
if (flag == NULL) {
} else if (value == NULL) {
} else {
retval += ProcessSingleOptionLocked(flag, value, set_mode);
}
} else { if (!in_filename_section) { in_filename_section = true;
flags_are_relevant = false;
}
const char* space = line.c_str(); for (const char* word = line.c_str(); *space; word = space+1) {
if (flags_are_relevant) break;
space = strchr(word, ' ');
if (space == NULL)
space = word + strlen(word);
const string glob(word, space - word);
if (glob == ProgramInvocationName() || glob == ProgramInvocationShortName()
#if defined(HAVE_FNMATCH_H)
|| fnmatch(glob.c_str(), ProgramInvocationName(), FNM_PATHNAME) == 0
|| fnmatch(glob.c_str(), ProgramInvocationShortName(), FNM_PATHNAME) == 0
#elif defined(HAVE_SHLWAPI_H)
|| PathMatchSpec(glob.c_str(), ProgramInvocationName())
|| PathMatchSpec(glob.c_str(), ProgramInvocationShortName())
#endif
) {
flags_are_relevant = true;
}
}
}
}
return retval;
}
template<typename T>
T GetFromEnv(const char *varname, const char* type, T dflt) {
std::string valstr;
if (SafeGetEnv(varname, valstr)) {
FlagValue ifv(new T, type, true);
if (!ifv.ParseFrom(valstr.c_str())) {
ReportError(DIE, "ERROR: error parsing env variable '%s' with value '%s'\n",
varname, valstr.c_str());
}
return OTHER_VALUE_AS(ifv, T);
} else return dflt;
}
bool AddFlagValidator(const void* flag_ptr, ValidateFnProto validate_fn_proto) {
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
FlagRegistryLock frl(registry);
CommandLineFlag* flag = registry->FindFlagViaPtrLocked(flag_ptr);
if (!flag) {
LOG(WARNING) << "Ignoring RegisterValidateFunction() for flag pointer "
<< flag_ptr << ": no flag found at that address";
return false;
} else if (validate_fn_proto == flag->validate_function()) {
return true; } else if (validate_fn_proto != NULL && flag->validate_function() != NULL) {
LOG(WARNING) << "Ignoring RegisterValidateFunction() for flag '"
<< flag->name() << "': validate-fn already registered";
return false;
} else {
flag->validate_fn_proto_ = validate_fn_proto;
return true;
}
}
}
FlagRegisterer::FlagRegisterer(const char* name, const char* type,
const char* help, const char* filename,
void* current_storage, void* defvalue_storage) {
if (help == NULL)
help = "";
if (strchr(type, ':'))
type = strrchr(type, ':') + 1;
FlagValue* current = new FlagValue(current_storage, type, false);
FlagValue* defvalue = new FlagValue(defvalue_storage, type, false);
CommandLineFlag* flag = new CommandLineFlag(name, help, filename,
current, defvalue);
FlagRegistry::GlobalRegistry()->RegisterFlag(flag); }
struct FilenameFlagnameCmp {
bool operator()(const CommandLineFlagInfo& a,
const CommandLineFlagInfo& b) const {
int cmp = strcmp(a.filename.c_str(), b.filename.c_str());
if (cmp == 0)
cmp = strcmp(a.name.c_str(), b.name.c_str()); return cmp < 0;
}
};
void GetAllFlags(vector<CommandLineFlagInfo>* OUTPUT) {
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
registry->Lock();
for (FlagRegistry::FlagConstIterator i = registry->flags_.begin();
i != registry->flags_.end(); ++i) {
CommandLineFlagInfo fi;
i->second->FillCommandLineFlagInfo(&fi);
OUTPUT->push_back(fi);
}
registry->Unlock();
sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameCmp());
}
static const char* argv0 = "UNKNOWN"; static const char* cmdline = ""; static vector<string> argvs;
static uint32 argv_sum = 0;
static const char* program_usage = NULL;
void SetArgv(int argc, const char** argv) {
static bool called_set_argv = false;
if (called_set_argv) return;
called_set_argv = true;
assert(argc > 0); argv0 = strdup(argv[0]); assert(argv0);
string cmdline_string; for (int i = 0; i < argc; i++) {
if (i != 0) {
cmdline_string += " ";
}
cmdline_string += argv[i];
argvs.push_back(argv[i]);
}
cmdline = strdup(cmdline_string.c_str()); assert(cmdline);
for (const char* c = cmdline; *c; c++)
argv_sum += *c;
}
const vector<string>& GetArgvs() { return argvs; }
const char* GetArgv() { return cmdline; }
const char* GetArgv0() { return argv0; }
uint32 GetArgvSum() { return argv_sum; }
const char* ProgramInvocationName() { return GetArgv0();
}
const char* ProgramInvocationShortName() { const char* slash = strrchr(argv0, '/');
#ifdef OS_WINDOWS
if (!slash) slash = strrchr(argv0, '\\');
#endif
return slash ? slash + 1 : argv0;
}
void SetUsageMessage(const string& usage) {
if (program_usage != NULL)
ReportError(DIE, "ERROR: SetUsageMessage() called twice\n");
program_usage = strdup(usage.c_str()); }
const char* ProgramUsage() {
if (program_usage) {
return program_usage;
}
return "Warning: SetUsageMessage() never called";
}
static const char* version_string = NULL;
void SetVersionString(const string& version) {
if (version_string != NULL)
ReportError(DIE, "ERROR: SetVersionString() called twice\n");
version_string = strdup(version.c_str()); }
const char* VersionString() {
return version_string ? version_string : "";
}
bool GetCommandLineOption(const char* name, string* value) {
if (NULL == name)
return false;
assert(value);
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
FlagRegistryLock frl(registry);
CommandLineFlag* flag = registry->FindFlagLocked(name);
if (flag == NULL) {
return false;
} else {
*value = flag->current_value();
return true;
}
}
bool GetCommandLineFlagInfo(const char* name, CommandLineFlagInfo* OUTPUT) {
if (NULL == name) return false;
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
FlagRegistryLock frl(registry);
CommandLineFlag* flag = registry->FindFlagLocked(name);
if (flag == NULL) {
return false;
} else {
assert(OUTPUT);
flag->FillCommandLineFlagInfo(OUTPUT);
return true;
}
}
CommandLineFlagInfo GetCommandLineFlagInfoOrDie(const char* name) {
CommandLineFlagInfo info;
if (!GetCommandLineFlagInfo(name, &info)) {
fprintf(stderr, "FATAL ERROR: flag name '%s' doesn't exist\n", name);
gflags_exitfunc(1); }
return info;
}
string SetCommandLineOptionWithMode(const char* name, const char* value,
FlagSettingMode set_mode) {
string result;
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
FlagRegistryLock frl(registry);
CommandLineFlag* flag = registry->FindFlagLocked(name);
if (flag) {
CommandLineFlagParser parser(registry);
result = parser.ProcessSingleOptionLocked(flag, value, set_mode);
if (!result.empty()) { }
}
return result;
}
string SetCommandLineOption(const char* name, const char* value) {
return SetCommandLineOptionWithMode(name, value, SET_FLAGS_VALUE);
}
class FlagSaverImpl {
public:
explicit FlagSaverImpl(FlagRegistry* main_registry)
: main_registry_(main_registry) { }
~FlagSaverImpl() {
vector<CommandLineFlag*>::const_iterator it;
for (it = backup_registry_.begin(); it != backup_registry_.end(); ++it)
delete *it;
}
void SaveFromRegistry() {
FlagRegistryLock frl(main_registry_);
assert(backup_registry_.empty()); for (FlagRegistry::FlagConstIterator it = main_registry_->flags_.begin();
it != main_registry_->flags_.end();
++it) {
const CommandLineFlag* main = it->second;
CommandLineFlag* backup = new CommandLineFlag(
main->name(), main->help(), main->filename(),
main->current_->New(), main->defvalue_->New());
backup->CopyFrom(*main);
backup_registry_.push_back(backup); }
}
void RestoreToRegistry() {
FlagRegistryLock frl(main_registry_);
vector<CommandLineFlag*>::const_iterator it;
for (it = backup_registry_.begin(); it != backup_registry_.end(); ++it) {
CommandLineFlag* main = main_registry_->FindFlagLocked((*it)->name());
if (main != NULL) { main->CopyFrom(**it);
}
}
}
private:
FlagRegistry* const main_registry_;
vector<CommandLineFlag*> backup_registry_;
FlagSaverImpl(const FlagSaverImpl&); void operator=(const FlagSaverImpl&);
};
FlagSaver::FlagSaver()
: impl_(new FlagSaverImpl(FlagRegistry::GlobalRegistry())) {
impl_->SaveFromRegistry();
}
FlagSaver::~FlagSaver() {
impl_->RestoreToRegistry();
delete impl_;
}
static string TheseCommandlineFlagsIntoString(
const vector<CommandLineFlagInfo>& flags) {
vector<CommandLineFlagInfo>::const_iterator i;
size_t retval_space = 0;
for (i = flags.begin(); i != flags.end(); ++i) {
retval_space += i->name.length() + i->current_value.length() + 5;
}
string retval;
retval.reserve(retval_space);
for (i = flags.begin(); i != flags.end(); ++i) {
retval += "--";
retval += i->name;
retval += "=";
retval += i->current_value;
retval += "\n";
}
return retval;
}
string CommandlineFlagsIntoString() {
vector<CommandLineFlagInfo> sorted_flags;
GetAllFlags(&sorted_flags);
return TheseCommandlineFlagsIntoString(sorted_flags);
}
bool ReadFlagsFromString(const string& flagfilecontents,
const char* , bool errors_are_fatal) {
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
FlagSaverImpl saved_states(registry);
saved_states.SaveFromRegistry();
CommandLineFlagParser parser(registry);
registry->Lock();
parser.ProcessOptionsFromStringLocked(flagfilecontents, SET_FLAGS_VALUE);
registry->Unlock();
HandleCommandLineHelpFlags();
if (parser.ReportErrors()) {
if (errors_are_fatal)
gflags_exitfunc(1);
saved_states.RestoreToRegistry();
return false;
}
return true;
}
bool AppendFlagsIntoFile(const string& filename, const char *prog_name) {
FILE *fp;
if (SafeFOpen(&fp, filename.c_str(), "a") != 0) {
return false;
}
if (prog_name)
fprintf(fp, "%s\n", prog_name);
vector<CommandLineFlagInfo> flags;
GetAllFlags(&flags);
vector<CommandLineFlagInfo>::iterator i;
for (i = flags.begin(); i != flags.end(); ++i) {
if (strcmp(i->name.c_str(), "flagfile") == 0) {
flags.erase(i);
break;
}
}
fprintf(fp, "%s", TheseCommandlineFlagsIntoString(flags).c_str());
fclose(fp);
return true;
}
bool ReadFromFlagsFile(const string& filename, const char* prog_name,
bool errors_are_fatal) {
return ReadFlagsFromString(ReadFileIntoString(filename.c_str()),
prog_name, errors_are_fatal);
}
bool BoolFromEnv(const char *v, bool dflt) {
return GetFromEnv(v, "bool", dflt);
}
int32 Int32FromEnv(const char *v, int32 dflt) {
return GetFromEnv(v, "int32", dflt);
}
int64 Int64FromEnv(const char *v, int64 dflt) {
return GetFromEnv(v, "int64", dflt);
}
uint64 Uint64FromEnv(const char *v, uint64 dflt) {
return GetFromEnv(v, "uint64", dflt);
}
double DoubleFromEnv(const char *v, double dflt) {
return GetFromEnv(v, "double", dflt);
}
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
const char *StringFromEnv(const char *varname, const char *dflt) {
const char* const val = getenv(varname);
return val ? val : dflt;
}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
bool RegisterFlagValidator(const bool* flag,
bool (*validate_fn)(const char*, bool)) {
return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
}
bool RegisterFlagValidator(const int32* flag,
bool (*validate_fn)(const char*, int32)) {
return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
}
bool RegisterFlagValidator(const int64* flag,
bool (*validate_fn)(const char*, int64)) {
return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
}
bool RegisterFlagValidator(const uint64* flag,
bool (*validate_fn)(const char*, uint64)) {
return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
}
bool RegisterFlagValidator(const double* flag,
bool (*validate_fn)(const char*, double)) {
return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
}
bool RegisterFlagValidator(const string* flag,
bool (*validate_fn)(const char*, const string&)) {
return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
}
static uint32 ParseCommandLineFlagsInternal(int* argc, char*** argv,
bool remove_flags, bool do_report) {
SetArgv(*argc, const_cast<const char**>(*argv));
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
CommandLineFlagParser parser(registry);
registry->Lock();
parser.ProcessFlagfileLocked(FLAGS_flagfile, SET_FLAGS_VALUE);
parser.ProcessFromenvLocked(FLAGS_fromenv, SET_FLAGS_VALUE, true);
parser.ProcessFromenvLocked(FLAGS_tryfromenv, SET_FLAGS_VALUE, false);
registry->Unlock();
const int r = parser.ParseNewCommandLineFlags(argc, argv, remove_flags);
if (do_report)
HandleCommandLineHelpFlags();
parser.ValidateAllFlags();
if (parser.ReportErrors()) gflags_exitfunc(1);
return r;
}
uint32 ParseCommandLineFlags(int* argc, char*** argv, bool remove_flags) {
return ParseCommandLineFlagsInternal(argc, argv, remove_flags, true);
}
uint32 ParseCommandLineNonHelpFlags(int* argc, char*** argv,
bool remove_flags) {
return ParseCommandLineFlagsInternal(argc, argv, remove_flags, false);
}
void AllowCommandLineReparsing() {
allow_command_line_reparsing = true;
}
void ReparseCommandLineNonHelpFlags() {
const vector<string>& argvs = GetArgvs();
int tmp_argc = static_cast<int>(argvs.size());
char** tmp_argv = new char* [tmp_argc + 1];
for (int i = 0; i < tmp_argc; ++i)
tmp_argv[i] = strdup(argvs[i].c_str());
ParseCommandLineNonHelpFlags(&tmp_argc, &tmp_argv, false);
for (int i = 0; i < tmp_argc; ++i)
free(tmp_argv[i]);
delete[] tmp_argv;
}
void ShutDownCommandLineFlags() {
FlagRegistry::DeleteGlobalRegistry();
}
}