Squashed 'third_party/ctemplate/' content from commit 6742f62

Change-Id: I828e4e4c906f13ba19944d78a8a78652b62949af
git-subtree-dir: third_party/ctemplate
git-subtree-split: 6742f6233db12f545e90baa8f34f5c29c4eb396a
diff --git a/src/make_tpl_varnames_h.cc b/src/make_tpl_varnames_h.cc
new file mode 100644
index 0000000..1dcb069
--- /dev/null
+++ b/src/make_tpl_varnames_h.cc
@@ -0,0 +1,468 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+//
+// A utility for checking syntax and generating headers to
+// use with Google Templates.
+//
+// For example:
+//
+// > <path_to>/make_tpl_varnames_h some_template_file.tpl
+//
+// This creates the header file some_template_file.tpl.varnames.h.  If
+// there are any syntax errors they are reported to stderr (in which
+// case, no header file is created).
+//
+//
+// Exit code is the number of templates we were unable to parse.
+//
+// Headers can be all written to one output file (via --outputfile)
+// or written to one output file per template processed (via --header_dir).
+// As such, we have a first stage where we load each template and generate
+// its headers and a second stage where we write the headers to disk.
+//
+// TODO(jad): Prevent -f and -o from being used together.
+//            Previously -o would be silently ignored.
+
+// This is for windows.  Even though we #include config.h, just like
+// the files used to compile the dll, we are actually a *client* of
+// the dll, so we don't get to decl anything.
+#include <config.h>
+#undef CTEMPLATE_DLL_DECL
+#include <ctype.h>    // for toupper(), isalnum()
+#include <errno.h>
+#ifdef HAVE_GETOPT_H
+# include <getopt.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <string>
+#include <set>
+#include <vector>
+
+#include <ctemplate/template_pathops.h>
+#include <ctemplate/template.h>
+using std::set;
+using std::string;
+using std::vector;
+using GOOGLE_NAMESPACE::Template;
+
+enum {LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL};
+
+// Holds information on each template we process.
+struct TemplateRecord {
+  const string name;       // filename given on cmd-line (may be relative
+  bool error;              // true iff an error occurred during template loading
+  string header_entries;   // output of tpl->WriteHeaderEntries()
+
+  explicit TemplateRecord(const string& aname)
+      : name(aname), error(false) {
+  }
+};
+
+static void LogPrintf(int severity, int should_log_info, const char* pat, ...) {
+  if (severity == LOG_INFO && !should_log_info)
+    return;
+  if (severity == LOG_FATAL)
+    fprintf(stderr, "FATAL ERROR: ");
+  va_list ap;
+  va_start(ap, pat);
+  vfprintf(stderr, pat, ap);
+  va_end(ap);
+  fprintf(stderr, "\n");
+  if (severity == LOG_FATAL)
+    exit(1);
+}
+
+// prints to outfile -- usually stdout or stderr
+static void Usage(const char* argv0, FILE* outfile) {
+  fprintf(outfile, "USAGE: %s [-t<dir>] [-o<dir>] [-s<suffix>] [-f<filename>]"
+          " [-n] [-d] [-q] <template_filename> ...\n", argv0);
+  fprintf(outfile,
+          "       -t<dir> --template_dir=<dir>  Root directory of templates\n"
+          "       -o<dir> --header_dir=<dir>    Where to place output files\n"
+          "       -s<suffix> --outputfile_suffix=<suffix>\n"
+          "                                     outname = inname + suffix\n"
+          "       -f<filename> --outputfile=<filename>\n"
+          "                                     outname = filename (when given, \n"
+          "                                     --header_dir is ignored)\n"
+          "       -n --noheader                 Just check syntax, no output\n"
+          "       -d --dump_templates           Cause templates dump contents\n"
+          "       -q --nolog_info               Only log on error\n"
+          "          --v=-1                     Obsolete, confusing synonym for -q\n"
+          "       -h --help                     This help\n"
+          "       -V --version                  Version information\n");
+  fprintf(outfile, "\n"
+          "This program checks the syntax of one or more google templates.\n"
+          "By default (without -n) it also emits a header file to an output\n"
+          "directory that defines all valid template keys.  This can be used\n"
+          "in programs to minimze the probability of typos in template code.\n");
+}
+
+static void Version(FILE* outfile) {
+  fprintf(outfile,
+          "make_tpl_varnames_h "
+          " (part of " PACKAGE_STRING ")"
+          "\n\n"
+          "Copyright 1998 Google Inc.\n"
+          "\n"
+          "This is BSD licensed software; see the source for copying conditions\n"
+          "and license information.\n"
+          "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
+          "PARTICULAR PURPOSE.\n"
+          );
+}
+
+// Removes all non alphanumeric characters from a string to form a
+// valid C identifier to use as a double-inclusion guard.
+static void ConvertToIdentifier(string* s) {
+  for (string::size_type i = 0; i < s->size(); i++) {
+    if (!isalnum((*s)[i]))
+      (*s)[i] = '_';
+    else
+      (*s)[i] = toupper((*s)[i]);
+  }
+}
+
+// Returns the given header entries wrapped with a compiler guard
+// whose name is generated from the output_file name.
+static string WrapWithGuard(const string& output_file,
+                            const string& header_entries) {
+  string guard(string("TPL_") + output_file);
+  ConvertToIdentifier(&guard);
+  guard.append("_H_");
+
+  string out;
+  out.append(string("#ifndef ") + guard + "\n");
+  out.append(string("#define ") + guard + "\n\n");
+
+  // Now append the header-entry info to the intro above
+  out.append(header_entries);
+
+  out.append(string("\n#endif  // ") + guard + "\n");
+  return out;
+}
+
+// Generates a multi-line comment that will go at the top of the output file.
+// The comment includes the filename(s) that produced the output, one per line.
+static string Boilerplate(const string& progname,
+                          const vector<string>& filenames) {
+  string out(string("//\n"));
+  if (filenames.size() > 1)
+    out.append("// This header file auto-generated for the templates\n");
+  else
+    out.append("// This header file auto-generated for the template\n");
+
+  for (vector<string>::size_type i = 0; i < filenames.size(); ++i)
+    out.append("//    " + filenames[i] + "\n");
+
+  out.append("// by " + progname + "\n" +
+             "// DO NOT MODIFY THIS FILE DIRECTLY\n" +
+             "//\n");
+  return out;
+}
+
+// Returns true iff line is empty or only contains whitespace
+// (space, horizontal tab, vertical tab, form feed, carriage return).
+static bool LineIsAllWhitespace(const string& input) {
+  static const string kWhitespace(" \f\t\v\r");
+  return input.find_first_not_of(kWhitespace) == string::npos;
+}
+
+// Splits the input string into lines using the newline (\n)
+// as delimiter. The newlines are discarded.
+// An empty string input results in one empty line output.
+//
+// Examples: "Hello\nWorld\n" input results in two lines,
+//           "Hello" and "World".
+//           Same result for "Hello\nWorld" (not newline terminated).
+//
+static vector<string> SplitIntoLines(const string &input) {
+  vector<string> lines;
+
+  string::size_type begin_index = 0;
+  string::size_type input_len = input.length();
+
+  while (1) {
+    string::size_type end_index = input.find_first_of('\n', begin_index);
+    if (end_index == string::npos) {
+        lines.push_back(input.substr(begin_index));
+      break;
+    }
+    lines.push_back(input.substr(begin_index, (end_index - begin_index)));
+    begin_index = end_index + 1;
+    if (begin_index >= input_len)  // To avoid adding a trailing empty line.
+      break;
+  }
+  return lines;
+}
+
+// Receives header entries concatenated together from one or more
+// templates and returns a string with the duplicate lines removed.
+//
+// Duplicate lines that contain only whitespace are not removed,
+// all other duplicate lines (identical #include directives and
+// identical variable definitions) are removed. If the last
+// (or only) input line did not terminate with newline, we add one.
+//
+// Consider the following two templates:
+//   ex1.tpl: <p>{{USER}}</p>
+//   ex2.tpl: <a href="{{URL}}">{{USER}}</a>
+//
+// The header entries for ex1.tpl are:
+// #include "template/template_string.h"
+// static const ::GOOGLE_NAMESPACE::StaticTemplateString ke_USER =
+//   STS_INIT_WITH_HASH(ke_USER, "USER", 3254611514008215315LLU);
+//
+// The header entries for ex2.tpl are:
+// #include "template/template_string.h"
+// static const ::GOOGLE_NAMESPACE::StaticTemplateString ke_URL =
+//   STS_INIT_WITH_HASH(ke_URL, "URL", 1026025273225241985LLU);
+// static const ::GOOGLE_NAMESPACE::StaticTemplateString ke_USER =
+//   STS_INIT_WITH_HASH(ke_USER, "USER", 3254611514008215315LLU);
+//
+// Simply concatenating both header entries will result in
+// duplicate #include directives and duplicate definitions of
+// the ke_USER variable. This function instead outputs:
+//
+// #include "template/template_string.h"
+// static const ::GOOGLE_NAMESPACE::StaticTemplateString ke_USER =
+//   STS_INIT_WITH_HASH(ke_USER, "USER", 3254611514008215315LLU);
+// static const ::GOOGLE_NAMESPACE::StaticTemplateString ke_URL =
+//   STS_INIT_WITH_HASH(ke_URL, "URL", 1026025273225241985LLU);
+//
+static string TextWithDuplicateLinesRemoved(const string& header_entries) {
+  string output;
+  set<string> lines_seen;
+  vector<string> lines = SplitIntoLines(header_entries);
+  const int lines_len = lines.size();
+  for (int i = 0; i < lines_len; ++i) {
+    const string& line = lines[i];
+    if (LineIsAllWhitespace(line) ||    // Blank lines always go in
+        !lines_seen.count(line)) {  // So do new lines
+      output.append(line);
+      output.append("\n");
+      lines_seen.insert(line);
+    }
+  }
+  return output;
+}
+
+// Writes the given text to the filename header_file.
+// Returns true if it succeeded, false otherwise.
+static bool WriteToDisk(bool log_info, const string& output_file,
+                        const string& text) {
+  FILE* outfile = fopen(output_file.c_str(), "wb");
+  if (!outfile) {
+    LogPrintf(LOG_ERROR, log_info, "Can't open %s", output_file.c_str());
+    return false;
+  }
+  LogPrintf(LOG_INFO, log_info, "Creating %s", output_file.c_str());
+  if (fwrite(text.c_str(), 1, text.length(), outfile) != text.length()) {
+    LogPrintf(LOG_ERROR, log_info, "Can't write %s: %s",
+              output_file.c_str(), strerror(errno));
+  }
+  fclose(outfile);
+  return true;
+}
+
+int main(int argc, char **argv) {
+  string FLAG_template_dir(GOOGLE_NAMESPACE::kCWD);   // "./"
+  string FLAG_header_dir(GOOGLE_NAMESPACE::kCWD);
+  GOOGLE_NAMESPACE::NormalizeDirectory(&FLAG_header_dir);   // adds trailing slash
+  string FLAG_outputfile_suffix(".varnames.h");
+  string FLAG_outputfile("");
+  bool FLAG_header = true;
+  bool FLAG_dump_templates = false;
+  bool FLAG_log_info = true;
+
+#if defined(HAVE_GETOPT_LONG)
+  static struct option longopts[] = {
+    {"help", 0, NULL, 'h'},
+    {"version", 0, NULL, 'V'},
+    {"template_dir", 1, NULL, 't'},
+    {"header_dir", 1, NULL, 'o'},
+    {"outputfile_suffix", 1, NULL, 's'},
+    {"outputfile", 1, NULL, 'f'},
+    {"noheader", 0, NULL, 'n'},
+    {"dump_templates", 0, NULL, 'd'},
+    {"nolog_info", 0, NULL, 'q'},
+    {"v", 1, NULL, 'q'},
+    {0, 0, 0, 0}
+  };
+  int option_index;
+# define GETOPT(argc, argv)  getopt_long(argc, argv, "t:o:s:f:ndqhV", \
+                                         longopts, &option_index)
+#elif defined(HAVE_GETOPT_H)
+# define GETOPT(argc, argv)  getopt(argc, argv, "t:o:s:f:ndqhV")
+#else    // TODO(csilvers): implement something reasonable for windows
+# define GETOPT(argc, argv)  -1
+  int optind = 1;    // first non-opt argument
+  const char* optarg = "";   // not used
+#endif
+
+  int r = 0;
+  while (r != -1) {   // getopt()/getopt_long() return -1 upon no-more-input
+    r = GETOPT(argc, argv);
+    switch (r) {
+      case 't': FLAG_template_dir.assign(optarg); break;
+      case 'o': FLAG_header_dir.assign(optarg); break;
+      case 's': FLAG_outputfile_suffix.assign(optarg); break;
+      case 'f': FLAG_outputfile.assign(optarg); break;
+      case 'n': FLAG_header = false; break;
+      case 'd': FLAG_dump_templates = true; break;
+      case 'q': FLAG_log_info = false; break;
+      case 'V': Version(stdout); return 0; break;
+      case 'h': Usage(argv[0], stderr); return 0; break;
+      case -1: break;   // means 'no more input'
+      default: Usage(argv[0], stderr); return 1; break;
+    }
+  }
+
+  if (optind >= argc) {
+    LogPrintf(LOG_FATAL, FLAG_log_info,
+              "Must specify at least one template file on the command line.");
+  }
+
+  Template::SetTemplateRootDirectory(FLAG_template_dir);
+
+
+  // Initialize the TemplateRecord array. It holds one element per
+  // template given on the command-line.
+  vector<TemplateRecord*> template_records;
+  for (int i = optind; i < argc; ++i) {
+    TemplateRecord *template_rec = new TemplateRecord(argv[i]);
+    template_records.push_back(template_rec);
+  }
+
+  // Iterate through each template and (unless -n is given), write
+  // its header entries into the headers array.
+  int num_errors = 0;
+  for (vector<TemplateRecord*>::iterator it = template_records.begin();
+       it != template_records.end(); ++it) {
+    const char* tplname = (*it)->name.c_str();
+    LogPrintf(LOG_INFO, FLAG_log_info, "\n------ Checking %s ------", tplname);
+
+    // The last two arguments in the following call do not matter
+    // since they control how the template gets expanded and we never
+    // expand the template after loading it here
+    Template * tpl = Template::GetTemplate(tplname, GOOGLE_NAMESPACE::DO_NOT_STRIP);
+
+    // The call to GetTemplate (above) loads the template from disk
+    // and attempts to parse it. If it cannot find the file or if it
+    // detects any template syntax errors, the parsing routines
+    // report the error and GetTemplate returns NULL. Syntax errors
+    // include such things as mismatched double-curly-bracket pairs,
+    // e.g. '{{VAR}', Invalid characters in template variables or
+    // section names, e.g.  '{{BAD_VAR?}}' [the question mark is
+    // illegal], improperly nested section/end section markers,
+    // e.g. a section close marker with no section start marker or a
+    // section start of a different name.
+    // If that happens, since the parsing errors have already been reported
+    // we just continue on to the next one.
+    if (!tpl) {
+      LogPrintf(LOG_ERROR, FLAG_log_info, "Could not load file: %s", tplname);
+      num_errors++;
+      (*it)->error = true;
+      continue;
+    } else {
+      LogPrintf(LOG_INFO, FLAG_log_info, "No syntax errors detected in %s",
+                tplname);
+      if (FLAG_dump_templates)
+        tpl->Dump(tpl->template_file());
+    }
+
+    // The rest of the loop creates the header file
+    if (!FLAG_header)
+      continue;            // They don't want header files
+
+    tpl->WriteHeaderEntries(&((*it)->header_entries));
+  }
+
+  // We have headers to emit:
+  // . If --outputfile was given, we combine all the header entries and
+  //   write them to the given output file. If any template had errors,
+  //   we fail and do not generate an output file.
+  // . Otherwise, we write one output file per template we processed.
+  // . In both cases, we add proper boilerplate first.
+  if (FLAG_header) {
+    string progname = argv[0];
+
+    if (!FLAG_outputfile.empty()) {  // All header entries written to one file.
+      // If any template had an error, we do not produce an output file.
+      if (num_errors == 0) {
+        vector<string> template_filenames;
+        string all_header_entries;
+        for (vector<TemplateRecord*>::const_iterator
+             it = template_records.begin(); it != template_records.end(); ++it) {
+          all_header_entries.append((*it)->header_entries);
+          template_filenames.push_back((*it)->name);
+        }
+        string output = Boilerplate(progname, template_filenames);
+        const string cleantext =
+            TextWithDuplicateLinesRemoved(all_header_entries);
+        output.append(WrapWithGuard(FLAG_outputfile, cleantext));
+        if (!WriteToDisk(FLAG_log_info, FLAG_outputfile, output))
+          num_errors++;
+      }
+    } else {
+      // Each template will have its own output file. Skip any that had errors.
+      for (vector<TemplateRecord*>::const_iterator
+           it = template_records.begin(); it != template_records.end(); ++it) {
+        if ((*it)->error)
+          continue;
+        string basename = GOOGLE_NAMESPACE::Basename((*it)->name);
+        string output_file =
+            GOOGLE_NAMESPACE::PathJoin(FLAG_header_dir,
+                                basename + FLAG_outputfile_suffix);
+        vector<string> template_filenames;   // Contains one template filename.
+        template_filenames.push_back((*it)->name);
+        string output = Boilerplate(progname, template_filenames);
+        output.append(WrapWithGuard(output_file, (*it)->header_entries));
+        if (!WriteToDisk(FLAG_log_info, output_file, output))
+          num_errors++;
+      }
+    }
+  }
+
+  // Free dynamic memory
+  for (vector<TemplateRecord*>::iterator it = template_records.begin();
+       it != template_records.end(); ++it) {
+    delete *it;
+  }
+
+  // Cap at 127 to avoid causing problems with return code
+  return num_errors > 127 ? 127 : num_errors;
+}