root/trunk/whisperlib/common/base/third-party/gflags_reporting.cc

Revision 7, 15.9 kB (checked in by whispercastorg, 2 years ago)

version 0.2.0

Line 
1 // Copyright (c) 2006, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 // ---
31 // Author: Ray Sidney
32 // Revamped and reorganized by Craig Silverstein
33 //
34 // This file contains code for handling the 'reporting' flags.  These
35 // are flags that, when present, cause the program to report some
36 // information and then exit.  --help and --version are the canonical
37 // reporting flags, but we also have flags like --helpxml, etc.
38 //
39 // There's only one function that's meant to be called externally:
40 // HandleCommandLineHelpFlags().  (Well, actually,
41 // ShowUsageWithFlags() and ShowUsageWithFlagsRestrict() can be called
42 // externally too, but there's little need for it.)  These are all
43 // declared in the main commandlineflags.h header file.
44 //
45 // HandleCommandLineHelpFlags() will check what 'reporting' flags have
46 // been defined, if any -- the "help" part of the function name is a
47 // bit misleading -- and do the relevant reporting.  It should be
48 // called after all flag-values have been assigned, that is, after
49 // parsing the command-line.
50
51 #include <stdio.h>
52 #include <string.h>
53 #include <ctype.h>
54 #include <assert.h>
55 #include <string>
56 #include <vector>
57 #include "common/base/gflags.h"
58 #include "common/base/strutil.h"
59 #include "common/base/system.h"
60
61 using std::string;
62 using std::vector;
63
64 // The 'reporting' flags.  They all call exit().
65 DEFINE_bool(help, false,
66             "show help on all flags [tip: all flags can have two dashes]");
67 DEFINE_bool(helpfull, false,
68             "show help on all flags -- same as -help");
69 DEFINE_bool(helpshort, false,
70             "show help on only the main module for this program");
71 DEFINE_string(helpon, "",
72               "show help on the modules named by this flag value");
73 DEFINE_string(helpmatch, "",
74               "show help on modules whose name contains the specified substr");
75 DEFINE_bool(helppackage, false,
76             "show help on all modules in the main package");
77 DEFINE_bool(helpxml, false,
78             "produce an xml version of help");
79 DEFINE_bool(version, false,
80             "show version and build info and exit");
81
82 namespace google {
83
84 // --------------------------------------------------------------------
85 // DescribeOneFlag()
86 // DescribeOneFlagInXML()
87 //    Routines that pretty-print info about a flag.  These use
88 //    a CommandLineFlagInfo, which is the way the commandlineflags
89 //    API exposes static info about a flag.
90 // --------------------------------------------------------------------
91
92 static const int kLineLength = 80;
93
94 static void AddString(const string& s,
95                       string* final_string, int* chars_in_line) {
96   const int slen = s.length();
97   if (*chars_in_line + 1 + slen >= kLineLength) {  // < 80 chars/line
98     *final_string += "\n      ";
99     *chars_in_line = 6;
100   } else {
101     *final_string += " ";
102     *chars_in_line += 1;
103   }
104   *final_string += s;
105   *chars_in_line += slen;
106 }
107
108 // Create a descriptive string for a flag.
109 // Goes to some trouble to make pretty line breaks.
110 static string DescribeOneFlag(const CommandLineFlagInfo& flag) {
111   string main_part = (string("    -") + flag.name +
112                       " (" + flag.description + ')');
113   const char* c_string = main_part.c_str();
114   int chars_left = main_part.length();
115   string final_string = "";
116   int chars_in_line = 0;  // how many chars in current line so far?
117   while (1) {
118     assert(chars_left == strlen(c_string));  // Unless there's a \0 in there?
119     const char* newline = strchr(c_string, '\n');
120     if (newline == NULL && chars_in_line+chars_left < kLineLength) {
121       // The whole remainder of the string fits on this line
122       final_string += c_string;
123       chars_in_line += chars_left;
124       break;
125     }
126     if (newline != NULL && newline - c_string < kLineLength - chars_in_line) {
127       int n = newline - c_string;
128       final_string.append(c_string, n);
129       chars_left -= n + 1;
130       c_string += n + 1;
131     } else {
132       // Find the last whitespace on this 80-char line
133       int whitespace = kLineLength-chars_in_line-1;  // < 80 chars/line
134       while ( whitespace > 0 && !isspace(c_string[whitespace]) ) {
135         --whitespace;
136       }
137       if (whitespace <= 0) {
138         // Couldn't find any whitespace to make a line break.  Just dump the
139         // rest out!
140         final_string += c_string;
141         chars_in_line = kLineLength;  // next part gets its own line for sure!
142         break;
143       }
144       final_string += string(c_string, whitespace);
145       chars_in_line += whitespace;
146       while (isspace(c_string[whitespace]))  ++whitespace;
147       c_string += whitespace;
148       chars_left -= whitespace;
149     }
150     if (*c_string == '\0')
151       break;
152     final_string += "\n      ";
153     chars_in_line = 6;
154   }
155
156   // Append data type
157   AddString(string("type: ") + flag.type, &final_string, &chars_in_line);
158   // Append the effective default value (i.e., the value that the flag
159   // will have after the command line is parsed if the flag is not
160   // specified on the command line), which may be different from the
161   // stored default value. This would happen if the value of the flag
162   // was modified before the command line was parsed. (Unless the
163   // value was modified using SetCommandLineOptionWithMode() with mode
164   // SET_FLAGS_DEFAULT.)
165   // Note that we are assuming this code is being executed because a help
166   // request was just parsed from the command line, in which case the
167   // printed value is indeed the effective default, as long as no value
168   // for the flag was parsed from the command line before "--help".
169   if (strcmp(flag.type.c_str(), "string") == 0) {  // add quotes for strings
170     AddString(string("default: \"") + flag.current_value + string("\""),
171               &final_string, &chars_in_line);
172   } else {
173     AddString(string("default: ") + flag.current_value,
174               &final_string, &chars_in_line);
175   }
176
177   final_string += '\n';
178   return final_string;
179 }
180
181 // Simple routine to xml-escape a string: escape & and < only.
182 static string XMLText(const string& txt) {
183   string ans = txt;
184   for (string::size_type pos = 0; (pos=ans.find("&", pos)) != string::npos; )
185     ans.replace(pos++, 1, "&amp;");
186   for (string::size_type pos = 0; (pos=ans.find("<", pos)) != string::npos; )
187     ans.replace(pos++, 1, "&lt;");
188   return ans;
189 }
190
191 static string DescribeOneFlagInXML(const CommandLineFlagInfo& flag) {
192   // The file and flagname could have been attributes, but default
193   // and meaning need to avoid attribute normalization.  This way it
194   // can be parsed by simple programs, in addition to xml parsers.
195   return (string("<flag>") +
196           "<file>" + XMLText(flag.filename) + "</file>" +
197           "<name>" + XMLText(flag.name) + "</name>" +
198           "<meaning>" + XMLText(flag.description) + "</meaning>" +
199           "<default>" + XMLText(flag.default_value) + "</default>" +
200           "<type>" + XMLText(flag.type) + "</type>" +
201           string("</flag>"));
202 }
203
204 // --------------------------------------------------------------------
205 // ShowUsageWithFlags()
206 // ShowUsageWithFlagsRestrict()
207 // ShowXMLOfFlags()
208 //    These routines variously expose the registry's list of flag
209 //    values.  ShowUsage*() prints the flag-value information
210 //    to stdout in a user-readable format (that's what --help uses).
211 //    The Restrict() version limits what flags are shown.
212 //    ShowXMLOfFlags() prints the flag-value information to stdout
213 //    in a machine-readable format.  In all cases, the flags are
214 //    sorted: first by filename they are defined in, then by flagname.
215 // --------------------------------------------------------------------
216
217 // Test whether a filename contains at least one of the substrings.
218 static bool FileMatchesSubstring(const string& filename,
219                                  const vector<string>& substrings) {
220   for (vector<string>::const_iterator target = substrings.begin();
221        target != substrings.end();
222        ++target) {
223     if (strstr(filename.c_str(), target->c_str()) != NULL) {
224       return true;
225     }
226   }
227   return false;
228 }
229
230 // Show help for every filename which matches any of the target substrings.
231 // If substrings is empty, shows help for every file. If a flag's help message
232 // has been stripped (e.g. by adding '#define STRIP_FLAG_HELP 1' to
233 // base/global_strip_options.h), then this flag will not be displayed by
234 // '--help' and its variants.
235 static void ShowUsageWithFlagsMatching(const char *argv0,
236                                        const vector<string> &substrings) {
237   fprintf(stdout, "%s: %s\n", strutil::Basename(argv0), ProgramUsage());
238
239   vector<CommandLineFlagInfo> flags;
240   GetAllFlags(&flags);           // flags are sorted by filename, then flagname
241
242   string last_filename = "";     // so we know when we're at a new file
243   bool first_directory = true;   // controls blank lines between dirs
244   bool found_match = false;      // stays false iff no dir matches restrict
245   for (vector<CommandLineFlagInfo>::const_iterator flag = flags.begin();
246        flag != flags.end();
247        ++flag) {
248     if (substrings.empty() ||
249         FileMatchesSubstring(flag->filename, substrings)) {
250       // If the flag has been stripped, pretend that it doesn't exist.
251       if (flag->description == kStrippedFlagHelp) continue;
252       found_match = true;     // this flag passed the match!
253       if (flag->filename != last_filename) {                      // new file
254         if (strutil::Dirname(flag->filename) !=
255             strutil::Dirname(last_filename)) {  // new dir!
256           if (!first_directory)
257             fprintf(stdout, "\n\n");   // put blank lines between directories
258           first_directory = false;
259         }
260         fprintf(stdout, "\n  Flags from %s:\n", flag->filename.c_str());
261         last_filename = flag->filename;
262       }
263       // Now print this flag
264       fprintf(stdout, "%s", DescribeOneFlag(*flag).c_str());
265     }
266   }
267   if (!found_match && !substrings.empty()) {
268     fprintf(stdout, "\n  No modules matched: use -help\n");
269   }
270 }
271
272 void ShowUsageWithFlagsRestrict(const char *argv0, const char *restrict) {
273   vector<string> substrings;
274   if (restrict != NULL && *restrict != '\0') {
275     substrings.push_back(restrict);
276   }
277   ShowUsageWithFlagsMatching(argv0, substrings);
278 }
279
280 void ShowUsageWithFlags(const char *argv0) {
281   ShowUsageWithFlagsRestrict(argv0, "");
282 }
283
284 // Convert the help, program, and usage to xml.
285 static void ShowXMLOfFlags(const char *prog_name) {
286   vector<CommandLineFlagInfo> flags;
287   GetAllFlags(&flags);   // flags are sorted: by filename, then flagname
288
289   // XML.  There is no corresponding schema yet
290   fprintf(stdout, "<?xml version=\"1.0\"?>\n");
291   // The document
292   fprintf(stdout, "<AllFlags>\n");
293   // the program name and usage
294   fprintf(stdout, "<program>%s</program>\n",
295           XMLText(strutil::Basename(prog_name)).c_str());
296   fprintf(stdout, "<usage>%s</usage>\n",
297           XMLText(ProgramUsage()).c_str());
298   // All the flags
299   for (vector<CommandLineFlagInfo>::const_iterator flag = flags.begin();
300        flag != flags.end();
301        ++flag) {
302     if (flag->description != kStrippedFlagHelp)
303       fprintf(stdout, "%s\n", DescribeOneFlagInXML(*flag).c_str());
304   }
305   // The end of the document
306   fprintf(stdout, "</AllFlags>\n");
307 }
308
309 // --------------------------------------------------------------------
310 // ShowVersion()
311 //    Called upon --version.  Prints build-related info.
312 // --------------------------------------------------------------------
313
314 static void ShowVersion() {
315   fprintf(stdout, "%s\n", ProgramInvocationShortName());
316   // TODO: add other stuff, like a timestamp, who built it, what
317   //       target they built, etc.
318
319 # if !defined(NDEBUG)
320   fprintf(stdout, "Debug build (NDEBUG not #defined)\n");
321 # endif
322 }
323
324 // --------------------------------------------------------------------
325 // HandleCommandLineHelpFlags()
326 //    Checks all the 'reporting' commandline flags to see if any
327 //    have been set.  If so, handles them appropriately.  Note
328 //    that all of them, by definition, cause the program to exit
329 //    if they trigger.
330 // --------------------------------------------------------------------
331
332 void HandleCommandLineHelpFlags() {
333   const char* progname = ProgramInvocationShortName();
334
335   if (FLAGS_helpshort) {
336     // show only flags related to this binary:
337     // E.g. for fileutil.cc, want flags containing   ... "/fileutil." cc
338     vector<string> substrings;
339     substrings.push_back(string("/") + progname + ".");
340     substrings.push_back(string("/") + progname + "-main.");
341     substrings.push_back(string("/") + progname + "_main.");
342     ShowUsageWithFlagsMatching(progname, substrings);
343     common::Exit(1);   // almost certainly exit()
344
345   } else if (FLAGS_help || FLAGS_helpfull) {
346     // show all options
347     ShowUsageWithFlagsRestrict(progname, "");   // empty restrict
348     common::Exit(1);
349
350   } else if (!FLAGS_helpon.empty()) {
351     string restrict = "/" + FLAGS_helpon + ".";
352     ShowUsageWithFlagsRestrict(progname, restrict.c_str());
353     common::Exit(1);
354
355   } else if (!FLAGS_helpmatch.empty()) {
356     ShowUsageWithFlagsRestrict(progname, FLAGS_helpmatch.c_str());
357     common::Exit(1);
358
359   } else if (FLAGS_helppackage) {
360     // Shows help for all files in the same directory as main().  We
361     // don't want to resort to looking at dirname(progname), because
362     // the user can pick progname, and it may not relate to the file
363     // where main() resides.  So instead, we search the flags for a
364     // filename like "/progname.cc", and take the dirname of that.
365     vector<CommandLineFlagInfo> flags;
366     GetAllFlags(&flags);
367     vector<string> substrings;
368     substrings.push_back(string("/") + progname + ".");
369     substrings.push_back(string("/") + progname + "-main.");
370     substrings.push_back(string("/") + progname + "_main.");
371     string last_package = "";
372     for (vector<CommandLineFlagInfo>::const_iterator flag = flags.begin();
373          flag != flags.end();
374          ++flag) {
375       if (!FileMatchesSubstring(flag->filename, substrings))
376         continue;
377       const string package = strutil::Dirname(flag->filename) + "/";
378       if (package != last_package) {
379         ShowUsageWithFlagsRestrict(progname, package.c_str());
380         if (last_package != "") {      // means this isn't our first pkg
381           fprintf(stderr, "WARNING: Multiple packages contain a file=%s\n",
382                   progname);
383         }
384         last_package = package;
385       }
386     }
387     if (last_package == "") {   // never found a package to print
388       fprintf(stderr, "WARNING: Unable to find a package for file=%s\n",
389               progname);
390     }
391     common::Exit(1);
392
393   } else if (FLAGS_helpxml) {
394     ShowXMLOfFlags(progname);
395     common::Exit(1);
396
397   } else if (FLAGS_version) {
398     ShowVersion();
399     // Unlike help, we may be asking for version in a script, so return 0
400     common::Exit(0);
401   }
402 }
403
404 }
Note: See TracBrowser for help on using the browser.