#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <string>
#include <vector>
#include "config.h"
#include "gflags.h"
#include "gflags_completions.h"
#include "util.h"
DEFINE_bool (help, false, "show help on all flags [tip: all flags can have two dashes]");
DEFINE_bool (helpfull, false, "show help on all flags -- same as -help");
DEFINE_bool (helpshort, false, "show help on only the main module for this program");
DEFINE_string(helpon, "", "show help on the modules named by this flag value");
DEFINE_string(helpmatch, "", "show help on modules whose name contains the specified substr");
DEFINE_bool (helppackage, false, "show help on all modules in the main package");
DEFINE_bool (helpxml, false, "produce an xml version of help");
DEFINE_bool (version, false, "show version and build info and exit");
namespace GFLAGS_NAMESPACE {
using std::string;
using std::vector;
static const int kLineLength = 80;
static void AddString(const string& s,
string* final_string, int* chars_in_line) {
const int slen = static_cast<int>(s.length());
if (*chars_in_line + 1 + slen >= kLineLength) { *final_string += "\n ";
*chars_in_line = 6;
} else {
*final_string += " ";
*chars_in_line += 1;
}
*final_string += s;
*chars_in_line += slen;
}
static string PrintStringFlagsWithQuotes(const CommandLineFlagInfo& flag,
const string& text, bool current) {
const char* c_string = (current ? flag.current_value.c_str() :
flag.default_value.c_str());
if (strcmp(flag.type.c_str(), "string") == 0) { return StringPrintf("%s: \"%s\"", text.c_str(), c_string);
} else {
return StringPrintf("%s: %s", text.c_str(), c_string);
}
}
string DescribeOneFlag(const CommandLineFlagInfo& flag) {
string main_part;
SStringPrintf(&main_part, " -%s (%s)",
flag.name.c_str(),
flag.description.c_str());
const char* c_string = main_part.c_str();
int chars_left = static_cast<int>(main_part.length());
string final_string = "";
int chars_in_line = 0; while (1) {
assert(chars_left == strlen(c_string)); const char* newline = strchr(c_string, '\n');
if (newline == NULL && chars_in_line+chars_left < kLineLength) {
final_string += c_string;
chars_in_line += chars_left;
break;
}
if (newline != NULL && newline - c_string < kLineLength - chars_in_line) {
int n = static_cast<int>(newline - c_string);
final_string.append(c_string, n);
chars_left -= n + 1;
c_string += n + 1;
} else {
int whitespace = kLineLength-chars_in_line-1; while ( whitespace > 0 && !isspace(c_string[whitespace]) ) {
--whitespace;
}
if (whitespace <= 0) {
final_string += c_string;
chars_in_line = kLineLength; break;
}
final_string += string(c_string, whitespace);
chars_in_line += whitespace;
while (isspace(c_string[whitespace])) ++whitespace;
c_string += whitespace;
chars_left -= whitespace;
}
if (*c_string == '\0')
break;
StringAppendF(&final_string, "\n ");
chars_in_line = 6;
}
AddString(string("type: ") + flag.type, &final_string, &chars_in_line);
AddString(PrintStringFlagsWithQuotes(flag, "default", false), &final_string,
&chars_in_line);
if (!flag.is_default) {
AddString(PrintStringFlagsWithQuotes(flag, "currently", true),
&final_string, &chars_in_line);
}
StringAppendF(&final_string, "\n");
return final_string;
}
static string XMLText(const string& txt) {
string ans = txt;
for (string::size_type pos = 0; (pos = ans.find("&", pos)) != string::npos; )
ans.replace(pos++, 1, "&");
for (string::size_type pos = 0; (pos = ans.find("<", pos)) != string::npos; )
ans.replace(pos++, 1, "<");
return ans;
}
static void AddXMLTag(string* r, const char* tag, const string& txt) {
StringAppendF(r, "<%s>%s</%s>", tag, XMLText(txt).c_str(), tag);
}
static string DescribeOneFlagInXML(const CommandLineFlagInfo& flag) {
string r("<flag>");
AddXMLTag(&r, "file", flag.filename);
AddXMLTag(&r, "name", flag.name);
AddXMLTag(&r, "meaning", flag.description);
AddXMLTag(&r, "default", flag.default_value);
AddXMLTag(&r, "current", flag.current_value);
AddXMLTag(&r, "type", flag.type);
r += "</flag>";
return r;
}
static const char* Basename(const char* filename) {
const char* sep = strrchr(filename, PATH_SEPARATOR);
return sep ? sep + 1 : filename;
}
static string Dirname(const string& filename) {
string::size_type sep = filename.rfind(PATH_SEPARATOR);
return filename.substr(0, (sep == string::npos) ? 0 : sep);
}
static bool FileMatchesSubstring(const string& filename,
const vector<string>& substrings) {
for (vector<string>::const_iterator target = substrings.begin();
target != substrings.end();
++target) {
if (strstr(filename.c_str(), target->c_str()) != NULL)
return true;
if (!target->empty() && (*target)[0] == PATH_SEPARATOR &&
strncmp(filename.c_str(), target->c_str() + 1,
strlen(target->c_str() + 1)) == 0)
return true;
}
return false;
}
static void ShowUsageWithFlagsMatching(const char *argv0,
const vector<string> &substrings) {
fprintf(stdout, "%s: %s\n", Basename(argv0), ProgramUsage());
vector<CommandLineFlagInfo> flags;
GetAllFlags(&flags);
string last_filename; bool first_directory = true; bool found_match = false; for (vector<CommandLineFlagInfo>::const_iterator flag = flags.begin();
flag != flags.end();
++flag) {
if (substrings.empty() ||
FileMatchesSubstring(flag->filename, substrings)) {
if (flag->description == kStrippedFlagHelp) continue;
found_match = true; if (flag->filename != last_filename) { if (Dirname(flag->filename) != Dirname(last_filename)) { if (!first_directory)
fprintf(stdout, "\n\n"); first_directory = false;
}
fprintf(stdout, "\n Flags from %s:\n", flag->filename.c_str());
last_filename = flag->filename;
}
fprintf(stdout, "%s", DescribeOneFlag(*flag).c_str());
}
}
if (!found_match && !substrings.empty()) {
fprintf(stdout, "\n No modules matched: use -help\n");
}
}
void ShowUsageWithFlagsRestrict(const char *argv0, const char *restrict) {
vector<string> substrings;
if (restrict != NULL && *restrict != '\0') {
substrings.push_back(restrict);
}
ShowUsageWithFlagsMatching(argv0, substrings);
}
void ShowUsageWithFlags(const char *argv0) {
ShowUsageWithFlagsRestrict(argv0, "");
}
static void ShowXMLOfFlags(const char *prog_name) {
vector<CommandLineFlagInfo> flags;
GetAllFlags(&flags);
fprintf(stdout, "<?xml version=\"1.0\"?>\n");
fprintf(stdout, "<AllFlags>\n");
fprintf(stdout, "<program>%s</program>\n",
XMLText(Basename(prog_name)).c_str());
fprintf(stdout, "<usage>%s</usage>\n",
XMLText(ProgramUsage()).c_str());
for (vector<CommandLineFlagInfo>::const_iterator flag = flags.begin();
flag != flags.end();
++flag) {
if (flag->description != kStrippedFlagHelp)
fprintf(stdout, "%s\n", DescribeOneFlagInXML(*flag).c_str());
}
fprintf(stdout, "</AllFlags>\n");
}
static void ShowVersion() {
const char* version_string = VersionString();
if (version_string && *version_string) {
fprintf(stdout, "%s version %s\n",
ProgramInvocationShortName(), version_string);
} else {
fprintf(stdout, "%s\n", ProgramInvocationShortName());
}
# if !defined(NDEBUG)
fprintf(stdout, "Debug build (NDEBUG not #defined)\n");
# endif
}
static void AppendPrognameStrings(vector<string>* substrings,
const char* progname) {
string r("");
r += PATH_SEPARATOR;
r += progname;
substrings->push_back(r + ".");
substrings->push_back(r + "-main.");
substrings->push_back(r + "_main.");
}
void HandleCommandLineHelpFlags() {
const char* progname = ProgramInvocationShortName();
HandleCommandLineCompletions();
vector<string> substrings;
AppendPrognameStrings(&substrings, progname);
if (FLAGS_helpshort) {
ShowUsageWithFlagsMatching(progname, substrings);
gflags_exitfunc(1);
} else if (FLAGS_help || FLAGS_helpfull) {
ShowUsageWithFlagsRestrict(progname, ""); gflags_exitfunc(1);
} else if (!FLAGS_helpon.empty()) {
string restrict = PATH_SEPARATOR + FLAGS_helpon + ".";
ShowUsageWithFlagsRestrict(progname, restrict.c_str());
gflags_exitfunc(1);
} else if (!FLAGS_helpmatch.empty()) {
ShowUsageWithFlagsRestrict(progname, FLAGS_helpmatch.c_str());
gflags_exitfunc(1);
} else if (FLAGS_helppackage) {
vector<CommandLineFlagInfo> flags;
GetAllFlags(&flags);
string last_package;
for (vector<CommandLineFlagInfo>::const_iterator flag = flags.begin();
flag != flags.end();
++flag) {
if (!FileMatchesSubstring(flag->filename, substrings))
continue;
const string package = Dirname(flag->filename) + PATH_SEPARATOR;
if (package != last_package) {
ShowUsageWithFlagsRestrict(progname, package.c_str());
VLOG(7) << "Found package: " << package;
if (!last_package.empty()) { LOG(WARNING) << "Multiple packages contain a file=" << progname;
}
last_package = package;
}
}
if (last_package.empty()) { LOG(WARNING) << "Unable to find a package for file=" << progname;
}
gflags_exitfunc(1);
} else if (FLAGS_helpxml) {
ShowXMLOfFlags(progname);
gflags_exitfunc(1);
} else if (FLAGS_version) {
ShowVersion();
gflags_exitfunc(0);
}
}
}