enh: Refactored Bazel BUILD rules
[platform/upstream/gflags.git] / src / gflags_completions.cc
index a573cfa..f772486 100644 (file)
@@ -28,8 +28,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
 // ---
-// Author: Dave Nicponski
-//
+
 // Bash-style command line flag completion for C++ binaries
 //
 // This module implements bash-style completions.  It achieves this
 //     5a) Force bash to place most-relevent groups at the top of the list
 //     5b) Trim most flag's descriptions to fit on a single terminal line
 
-
-#include "config.h"
+#include <stdio.h>
 #include <stdlib.h>
+#include <string.h>   // for strlen
 
 #include <set>
 #include <string>
 #include <utility>
 #include <vector>
 
-#include "google/gflags.h"
+#include "config.h"
+#include "gflags/gflags.h"
+#include "gflags/gflags_completions.h"
+#include "util.h"
 
 using std::set;
 using std::string;
 using std::vector;
 
-#ifndef PATH_SEPARATOR
-#define PATH_SEPARATOR  '/'
-#endif
 
 DEFINE_string(tab_completion_word, "",
               "If non-empty, HandleCommandLineCompletions() will hijack the "
@@ -73,10 +72,11 @@ DEFINE_string(tab_completion_word, "",
 DEFINE_int32(tab_completion_columns, 80,
              "Number of columns to use in output for tab completion");
 
-_START_GOOGLE_NAMESPACE_
 
-namespace {
+namespace GFLAGS_NAMESPACE {
+
 
+namespace {
 // Function prototypes and Type forward declarations.  Code may be
 // more easily understood if it is roughly ordered according to
 // control flow, rather than by C's "declare before use" ordering
@@ -119,7 +119,7 @@ static void CategorizeAllMatchingFlags(
     NotableFlags *notable_flags);
 
 static void TryFindModuleAndPackageDir(
-    const vector<CommandLineFlagInfo> all_flags,
+    const vector<CommandLineFlagInfo> &all_flags,
     string *module,
     string *package_dir);
 
@@ -145,7 +145,7 @@ static void OutputSingleGroupWithLimit(
     const string &footer,
     bool long_output_format,
     int *remaining_line_limit,
-    int *completion_elements_added,
+    size_t *completion_elements_added,
     vector<string> *completions);
 
 // (helpers for #5)
@@ -202,18 +202,18 @@ struct NotableFlags {
 static void PrintFlagCompletionInfo(void) {
   string cursor_word = FLAGS_tab_completion_word;
   string canonical_token;
-  CompletionOptions options;
+  CompletionOptions options = { };
   CanonicalizeCursorWordAndSearchOptions(
       cursor_word,
       &canonical_token,
       &options);
 
-  //VLOG(1) << "Identified canonical_token: '" << canonical_token << "'";
+  DVLOG(1) << "Identified canonical_token: '" << canonical_token << "'";
 
   vector<CommandLineFlagInfo> all_flags;
   set<const CommandLineFlagInfo *> matching_flags;
   GetAllFlags(&all_flags);
-  //VLOG(2) << "Found " << all_flags.size() << " flags overall";
+  DVLOG(2) << "Found " << all_flags.size() << " flags overall";
 
   string longest_common_prefix;
   FindMatchingFlags(
@@ -222,28 +222,28 @@ static void PrintFlagCompletionInfo(void) {
       canonical_token,
       &matching_flags,
       &longest_common_prefix);
-  //VLOG(1) << "Identified " << matching_flags.size() << " matching flags";
-  //VLOG(1) << "Identified " << longest_common_prefix
-  //        << " as longest common prefix.";
+  DVLOG(1) << "Identified " << matching_flags.size() << " matching flags";
+  DVLOG(1) << "Identified " << longest_common_prefix
+          << " as longest common prefix.";
   if (longest_common_prefix.size() > canonical_token.size()) {
     // There's actually a shared common prefix to all matching flags,
     // so may as well output that and quit quickly.
-    //VLOG(1) << "The common prefix '" << longest_common_prefix
-    //        << "' was longer than the token '" << canonical_token
-    //        << "'.  Returning just this prefix for completion.";
+    DVLOG(1) << "The common prefix '" << longest_common_prefix
+            << "' was longer than the token '" << canonical_token
+            << "'.  Returning just this prefix for completion.";
     fprintf(stdout, "--%s", longest_common_prefix.c_str());
     return;
   }
   if (matching_flags.empty()) {
-    //VLOG(1) << "There were no matching flags, returning nothing.";
+    VLOG(1) << "There were no matching flags, returning nothing.";
     return;
   }
 
   string module;
   string package_dir;
   TryFindModuleAndPackageDir(all_flags, &module, &package_dir);
-  //VLOG(1) << "Identified module: '" << module << "'";
-  //VLOG(1) << "Identified package_dir: '" << package_dir << "'";
+  DVLOG(1) << "Identified module: '" << module << "'";
+  DVLOG(1) << "Identified package_dir: '" << package_dir << "'";
 
   NotableFlags notable_flags;
   CategorizeAllMatchingFlags(
@@ -252,12 +252,12 @@ static void PrintFlagCompletionInfo(void) {
       module,
       package_dir,
       &notable_flags);
-  //VLOG(2) << "Categorized matching flags:";
-  //VLOG(2) << " perfect_match: " << notable_flags.perfect_match_flag.size();
-  //VLOG(2) << " module: " << notable_flags.module_flags.size();
-  //VLOG(2) << " package: " << notable_flags.package_flags.size();
-  //VLOG(2) << " most common: " << notable_flags.most_common_flags.size();
-  //VLOG(2) << " subpackage: " << notable_flags.subpackage_flags.size();
+  DVLOG(2) << "Categorized matching flags:";
+  DVLOG(2) << " perfect_match: " << notable_flags.perfect_match_flag.size();
+  DVLOG(2) << " module: " << notable_flags.module_flags.size();
+  DVLOG(2) << " package: " << notable_flags.package_flags.size();
+  DVLOG(2) << " most common: " << notable_flags.most_common_flags.size();
+  DVLOG(2) << " subpackage: " << notable_flags.subpackage_flags.size();
 
   vector<string> completions;
   FinalizeCompletionOutput(
@@ -269,13 +269,13 @@ static void PrintFlagCompletionInfo(void) {
   if (options.force_no_update)
     completions.push_back("~");
 
-  //VLOG(1) << "Finalized with " << completions.size()
-  //        << " chosen completions";
+  DVLOG(1) << "Finalized with " << completions.size()
+          << " chosen completions";
 
   for (vector<string>::const_iterator it = completions.begin();
       it != completions.end();
       ++it) {
-    //VLOG(9) << "  Completion entry: '" << *it << "'";
+    DVLOG(9) << "  Completion entry: '" << *it << "'";
     fprintf(stdout, "%s\n", it->c_str());
   }
 }
@@ -395,7 +395,7 @@ static bool DoesSingleFlagMatch(
       flag.filename.find(match_token) != string::npos)
     return true;
 
-  // TODO(daven): All searches should probably be case-insensitive
+  // TODO(user): All searches should probably be case-insensitive
   // (especially this one...)
   if (options.flag_description_substring_search &&
       flag.description.find(match_token) != string::npos)
@@ -424,8 +424,8 @@ static void CategorizeAllMatchingFlags(
         all_matches.begin();
       it != all_matches.end();
       ++it) {
-    //VLOG(2) << "Examinging match '" << (*it)->name << "'";
-    //VLOG(7) << "  filename: '" << (*it)->filename << "'";
+    DVLOG(2) << "Examining match '" << (*it)->name << "'";
+    DVLOG(7) << "  filename: '" << (*it)->filename << "'";
     string::size_type pos = string::npos;
     if (!package_dir.empty())
       pos = (*it)->filename.find(package_dir);
@@ -438,52 +438,56 @@ static void CategorizeAllMatchingFlags(
     if ((*it)->name == search_token) {
       // Exact match on some flag's name
       notable_flags->perfect_match_flag.insert(*it);
-      //VLOG(3) << "Result: perfect match";
+      DVLOG(3) << "Result: perfect match";
     } else if (!module.empty() && (*it)->filename == module) {
       // Exact match on module filename
       notable_flags->module_flags.insert(*it);
-      //VLOG(3) << "Result: module match";
+      DVLOG(3) << "Result: module match";
     } else if (!package_dir.empty() &&
         pos != string::npos && slash == string::npos) {
       // In the package, since there was no slash after the package portion
       notable_flags->package_flags.insert(*it);
-      //VLOG(3) << "Result: package match";
+      DVLOG(3) << "Result: package match";
     } else if (false) {
       // In the list of the XXX most commonly supplied flags overall
-      // TODO(daven): Compile this list.
-      //VLOG(3) << "Result: most-common match";
+      // TODO(user): Compile this list.
+      DVLOG(3) << "Result: most-common match";
     } else if (!package_dir.empty() &&
         pos != string::npos && slash != string::npos) {
       // In a subdirectory of the package
       notable_flags->subpackage_flags.insert(*it);
-      //VLOG(3) << "Result: subpackage match";
+      DVLOG(3) << "Result: subpackage match";
     }
 
-    //VLOG(3) << "Result: not special match";
+    DVLOG(3) << "Result: not special match";
   }
 }
 
+static void PushNameWithSuffix(vector<string>* suffixes, const char* suffix) {
+  suffixes->push_back(
+      StringPrintf("/%s%s", ProgramInvocationShortName(), suffix));
+}
+
 static void TryFindModuleAndPackageDir(
-    const vector<CommandLineFlagInfo> all_flags,
+    const vector<CommandLineFlagInfo> &all_flags,
     string *module,
     string *package_dir) {
   module->clear();
   package_dir->clear();
 
   vector<string> suffixes;
-  // TODO(daven): There's some inherant ambiguity here - multiple directories
+  // TODO(user): There's some inherant ambiguity here - multiple directories
   // could share the same trailing folder and file structure (and even worse,
   // same file names), causing us to be unsure as to which of the two is the
   // actual package for this binary.  In this case, we'll arbitrarily choose.
-  string progname(ProgramInvocationShortName());
-  suffixes.push_back("/" + progname + ".");
-  suffixes.push_back("/" + progname + "-main.");
-  suffixes.push_back("/" + progname + "_main.");
+  PushNameWithSuffix(&suffixes, ".");
+  PushNameWithSuffix(&suffixes, "-main.");
+  PushNameWithSuffix(&suffixes, "_main.");
   // These four are new but probably merited?
-  suffixes.push_back("/" + progname + "_test.");
-  suffixes.push_back("/" + progname + "-test.");
-  suffixes.push_back("/" + progname + "_unittest.");
-  suffixes.push_back("/" + progname + "-unittest.");
+  PushNameWithSuffix(&suffixes, "-test.");
+  PushNameWithSuffix(&suffixes, "_test.");
+  PushNameWithSuffix(&suffixes, "-unittest.");
+  PushNameWithSuffix(&suffixes, "_unittest.");
 
   for (vector<CommandLineFlagInfo>::const_iterator it = all_flags.begin();
       it != all_flags.end();
@@ -491,7 +495,7 @@ static void TryFindModuleAndPackageDir(
     for (vector<string>::const_iterator suffix = suffixes.begin();
         suffix != suffixes.end();
         ++suffix) {
-      // TODO(daven): Make sure the match is near the end of the string
+      // TODO(user): Make sure the match is near the end of the string
       if (it->filename.find(*suffix) != string::npos) {
         *module = it->filename;
         string::size_type sep = it->filename.rfind(PATH_SEPARATOR);
@@ -504,9 +508,20 @@ static void TryFindModuleAndPackageDir(
 
 // Can't specialize template type on a locally defined type.  Silly C++...
 struct DisplayInfoGroup {
-  string header;
-  string footer;
+  const char* header;
+  const char* footer;
   set<const CommandLineFlagInfo *> *group;
+
+  int SizeInLines() const {
+    int size_in_lines = static_cast<int>(group->size()) + 1;
+    if (strlen(header) > 0) {
+      size_in_lines++;
+    }
+    if (strlen(footer) > 0) {
+      size_in_lines++;
+    }
+    return size_in_lines;
+  }
 };
 
 // 4) Finalize and trim output flag set
@@ -533,45 +548,47 @@ static void FinalizeCompletionOutput(
   if (lines_so_far < max_desired_lines &&
       !notable_flags->perfect_match_flag.empty()) {
     perfect_match_found = true;
-    lines_so_far += notable_flags->perfect_match_flag.size() + 2;  // no header
     DisplayInfoGroup group =
-        { "", "==========", &notable_flags->perfect_match_flag };
+        { "",
+          "==========",
+          &notable_flags->perfect_match_flag };
+    lines_so_far += group.SizeInLines();
     output_groups.push_back(group);
   }
   if (lines_so_far < max_desired_lines &&
       !notable_flags->module_flags.empty()) {
-    lines_so_far += notable_flags->module_flags.size() + 3;
     DisplayInfoGroup group = {
         "-* Matching module flags *-",
         "===========================",
         &notable_flags->module_flags };
+    lines_so_far += group.SizeInLines();
     output_groups.push_back(group);
   }
   if (lines_so_far < max_desired_lines &&
       !notable_flags->package_flags.empty()) {
-    lines_so_far += notable_flags->package_flags.size() + 3;
     DisplayInfoGroup group = {
         "-* Matching package flags *-",
         "============================",
         &notable_flags->package_flags };
+    lines_so_far += group.SizeInLines();
     output_groups.push_back(group);
   }
   if (lines_so_far < max_desired_lines &&
       !notable_flags->most_common_flags.empty()) {
-    lines_so_far += notable_flags->most_common_flags.size() + 3;
     DisplayInfoGroup group = {
         "-* Commonly used flags *-",
         "=========================",
         &notable_flags->most_common_flags };
+    lines_so_far += group.SizeInLines();
     output_groups.push_back(group);
   }
   if (lines_so_far < max_desired_lines &&
       !notable_flags->subpackage_flags.empty()) {
-    lines_so_far += notable_flags->subpackage_flags.size() + 3;
     DisplayInfoGroup group = {
         "-* Matching sub-package flags *-",
         "================================",
         &notable_flags->subpackage_flags };
+    lines_so_far += group.SizeInLines();
     output_groups.push_back(group);
   }
 
@@ -579,11 +596,11 @@ static void FinalizeCompletionOutput(
   if (lines_so_far < max_desired_lines) {
     RetrieveUnusedFlags(matching_flags, *notable_flags, &obscure_flags);
     if (!obscure_flags.empty()) {
-      lines_so_far += obscure_flags.size() + 2;  // no footer
       DisplayInfoGroup group = {
           "-* Other flags *-",
           "",
           &obscure_flags };
+      lines_so_far += group.SizeInLines();
       output_groups.push_back(group);
     }
   }
@@ -591,8 +608,8 @@ static void FinalizeCompletionOutput(
   // Second, go through each of the chosen output groups and output
   // as many of those flags as we can, while remaining below our limit
   int remaining_lines = max_desired_lines;
-  int completions_output = 0;
-  int indent = output_groups.size() - 1;
+  size_t completions_output = 0;
+  int indent = static_cast<int>(output_groups.size()) - 1;
   for (vector<DisplayInfoGroup>::const_iterator it =
         output_groups.begin();
       it != output_groups.end();
@@ -600,8 +617,8 @@ static void FinalizeCompletionOutput(
     OutputSingleGroupWithLimit(
         *it->group,  // group
         string(indent, ' '),  // line indentation
-        it->header,  // header
-        it->footer,  // footer
+        string(it->header),  // header
+        string(it->footer),  // footer
         perfect_match_found,  // long format
         &remaining_lines,  // line limit - reduces this by number printed
         &completions_output,  // completions (not lines) added
@@ -637,7 +654,7 @@ static void RetrieveUnusedFlags(
   }
 }
 
-// 5) Output matches (and helpfer methods)
+// 5) Output matches (and helper methods)
 
 static void OutputSingleGroupWithLimit(
     const set<const CommandLineFlagInfo *> &group,
@@ -646,7 +663,7 @@ static void OutputSingleGroupWithLimit(
     const string &footer,
     bool long_output_format,
     int *remaining_line_limit,
-    int *completion_elements_output,
+    size_t *completion_elements_output,
     vector<string> *completions) {
   if (group.empty()) return;
   if (!header.empty()) {
@@ -675,17 +692,20 @@ static void OutputSingleGroupWithLimit(
 static string GetShortFlagLine(
     const string &line_indentation,
     const CommandLineFlagInfo &info) {
-  string prefix =
-    line_indentation + "--" + info.name + " [" +
-    (info.type == "string" ?
-       ("'" + info.default_value + "'") :
-       info.default_value)
-    + "] ";
-  int remainder = FLAGS_tab_completion_columns - prefix.size();
-  string suffix = "";
+  string prefix;
+  bool is_string = (info.type == "string");
+  SStringPrintf(&prefix, "%s--%s [%s%s%s] ",
+                line_indentation.c_str(),
+                info.name.c_str(),
+                (is_string ? "'" : ""),
+                info.default_value.c_str(),
+                (is_string ? "'" : ""));
+  int remainder =
+      FLAGS_tab_completion_columns - static_cast<int>(prefix.size());
+  string suffix;
   if (remainder > 0)
     suffix =
-        (info.description.size() > remainder ?
+        (static_cast<int>(info.description.size()) > remainder ?
          (info.description.substr(0, remainder - 3) + "...").c_str() :
          info.description.c_str());
   return prefix + suffix;
@@ -709,8 +729,12 @@ static string GetLongFlagLine(
   static const char kNewlineWithIndent[] = "\n    ";
   output.replace(output.find(" type:"), 1, string(kNewlineWithIndent));
   output.replace(output.find(" default:"), 1, string(kNewlineWithIndent));
-  output = line_indentation + " Details for '--" + info.name + "':\n" +
-     output + "    defined: " + info.filename;
+  output = StringPrintf("%s Details for '--%s':\n"
+                        "%s    defined: %s",
+                        line_indentation.c_str(),
+                        info.name.c_str(),
+                        output.c_str(),
+                        info.filename.c_str());
 
   // Eliminate any doubled newlines that crept in.  Specifically, if
   // DescribeOneFlag() decided to break the line just before "type"
@@ -726,7 +750,7 @@ static string GetLongFlagLine(
   for (string::size_type newline = output.find('\n');
       newline != string::npos;
       newline = output.find('\n')) {
-    int newline_pos = newline % FLAGS_tab_completion_columns;
+    int newline_pos = static_cast<int>(newline) % FLAGS_tab_completion_columns;
     int missing_spaces = FLAGS_tab_completion_columns - newline_pos;
     output.replace(newline, 1, line_of_spaces, 1, missing_spaces);
   }
@@ -737,7 +761,8 @@ static string GetLongFlagLine(
 void HandleCommandLineCompletions(void) {
   if (FLAGS_tab_completion_word.empty()) return;
   PrintFlagCompletionInfo();
-  exit(0);
+  gflags_exitfunc(0);
 }
 
-_END_GOOGLE_NAMESPACE_
+
+} // namespace GFLAGS_NAMESPACE