LCOV - code coverage report
Current view: top level - third_party/protobuf/src/google/protobuf/compiler - command_line_interface.cc (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 273 705 38.7 %
Date: 2015-10-10 Functions: 33 51 64.7 %

          Line data    Source code
       1             : // Protocol Buffers - Google's data interchange format
       2             : // Copyright 2008 Google Inc.  All rights reserved.
       3             : // https://developers.google.com/protocol-buffers/
       4             : //
       5             : // Redistribution and use in source and binary forms, with or without
       6             : // modification, are permitted provided that the following conditions are
       7             : // met:
       8             : //
       9             : //     * Redistributions of source code must retain the above copyright
      10             : // notice, this list of conditions and the following disclaimer.
      11             : //     * Redistributions in binary form must reproduce the above
      12             : // copyright notice, this list of conditions and the following disclaimer
      13             : // in the documentation and/or other materials provided with the
      14             : // distribution.
      15             : //     * Neither the name of Google Inc. nor the names of its
      16             : // contributors may be used to endorse or promote products derived from
      17             : // this software without specific prior written permission.
      18             : //
      19             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      20             : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      21             : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      22             : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      23             : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      24             : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      25             : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      26             : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      27             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      28             : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      29             : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      30             : 
      31             : // Author: kenton@google.com (Kenton Varda)
      32             : //  Based on original Protocol Buffers design by
      33             : //  Sanjay Ghemawat, Jeff Dean, and others.
      34             : 
      35             : #include <google/protobuf/compiler/command_line_interface.h>
      36             : 
      37             : #include <stdio.h>
      38             : #include <sys/types.h>
      39             : #include <sys/stat.h>
      40             : #include <fcntl.h>
      41             : #ifdef _MSC_VER
      42             : #include <io.h>
      43             : #include <direct.h>
      44             : #else
      45             : #include <unistd.h>
      46             : #endif
      47             : #include <errno.h>
      48             : #include <iostream>
      49             : #include <ctype.h>
      50             : 
      51             : #include <memory>
      52             : #ifndef _SHARED_PTR_H
      53             : #include <google/protobuf/stubs/shared_ptr.h>
      54             : #endif
      55             : 
      56             : #ifdef __APPLE__
      57             : #include <mach-o/dyld.h>
      58             : #endif
      59             : 
      60             : #include <google/protobuf/stubs/common.h>
      61             : #include <google/protobuf/stubs/stringprintf.h>
      62             : #include <google/protobuf/compiler/importer.h>
      63             : #include <google/protobuf/compiler/code_generator.h>
      64             : #include <google/protobuf/compiler/plugin.pb.h>
      65             : #include <google/protobuf/compiler/subprocess.h>
      66             : #include <google/protobuf/compiler/zip_writer.h>
      67             : #include <google/protobuf/descriptor.h>
      68             : #include <google/protobuf/text_format.h>
      69             : #include <google/protobuf/dynamic_message.h>
      70             : #include <google/protobuf/io/coded_stream.h>
      71             : #include <google/protobuf/io/zero_copy_stream_impl.h>
      72             : #include <google/protobuf/io/printer.h>
      73             : #include <google/protobuf/stubs/logging.h>
      74             : #include <google/protobuf/stubs/strutil.h>
      75             : #include <google/protobuf/stubs/substitute.h>
      76             : #include <google/protobuf/stubs/map_util.h>
      77             : #include <google/protobuf/stubs/stl_util.h>
      78             : 
      79             : 
      80             : namespace google {
      81             : namespace protobuf {
      82             : namespace compiler {
      83             : 
      84             : #if defined(_WIN32)
      85             : #define mkdir(name, mode) mkdir(name)
      86             : #ifndef W_OK
      87             : #define W_OK 02  // not defined by MSVC for whatever reason
      88             : #endif
      89             : #ifndef F_OK
      90             : #define F_OK 00  // not defined by MSVC for whatever reason
      91             : #endif
      92             : #ifndef STDIN_FILENO
      93             : #define STDIN_FILENO 0
      94             : #endif
      95             : #ifndef STDOUT_FILENO
      96             : #define STDOUT_FILENO 1
      97             : #endif
      98             : #endif
      99             : 
     100             : #ifndef O_BINARY
     101             : #ifdef _O_BINARY
     102             : #define O_BINARY _O_BINARY
     103             : #else
     104             : #define O_BINARY 0     // If this isn't defined, the platform doesn't need it.
     105             : #endif
     106             : #endif
     107             : 
     108             : namespace {
     109             : #if defined(_WIN32) && !defined(__CYGWIN__)
     110             : static const char* kPathSeparator = ";";
     111             : #else
     112             : static const char* kPathSeparator = ":";
     113             : #endif
     114             : 
     115             : // Returns true if the text looks like a Windows-style absolute path, starting
     116             : // with a drive letter.  Example:  "C:\foo".  TODO(kenton):  Share this with
     117             : // copy in importer.cc?
     118             : static bool IsWindowsAbsolutePath(const string& text) {
     119             : #if defined(_WIN32) || defined(__CYGWIN__)
     120             :   return text.size() >= 3 && text[1] == ':' &&
     121             :          isalpha(text[0]) &&
     122             :          (text[2] == '/' || text[2] == '\\') &&
     123             :          text.find_last_of(':') == 1;
     124             : #else
     125             :   return false;
     126             : #endif
     127             : }
     128             : 
     129             : void SetFdToTextMode(int fd) {
     130             : #ifdef _WIN32
     131             :   if (_setmode(fd, _O_TEXT) == -1) {
     132             :     // This should never happen, I think.
     133             :     GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_TEXT): " << strerror(errno);
     134             :   }
     135             : #endif
     136             :   // (Text and binary are the same on non-Windows platforms.)
     137             : }
     138             : 
     139             : void SetFdToBinaryMode(int fd) {
     140             : #ifdef _WIN32
     141             :   if (_setmode(fd, _O_BINARY) == -1) {
     142             :     // This should never happen, I think.
     143             :     GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_BINARY): " << strerror(errno);
     144             :   }
     145             : #endif
     146             :   // (Text and binary are the same on non-Windows platforms.)
     147             : }
     148             : 
     149          17 : void AddTrailingSlash(string* path) {
     150          17 :   if (!path->empty() && path->at(path->size() - 1) != '/') {
     151          17 :     path->push_back('/');
     152             :   }
     153          17 : }
     154             : 
     155          17 : bool VerifyDirectoryExists(const string& path) {
     156          17 :   if (path.empty()) return true;
     157             : 
     158          17 :   if (access(path.c_str(), F_OK) == -1) {
     159           0 :     std::cerr << path << ": " << strerror(errno) << std::endl;
     160           0 :     return false;
     161             :   } else {
     162             :     return true;
     163             :   }
     164             : }
     165             : 
     166             : // Try to create the parent directory of the given file, creating the parent's
     167             : // parent if necessary, and so on.  The full file name is actually
     168             : // (prefix + filename), but we assume |prefix| already exists and only create
     169             : // directories listed in |filename|.
     170         114 : bool TryCreateParentDirectory(const string& prefix, const string& filename) {
     171             :   // Recursively create parent directories to the output file.
     172         114 :   vector<string> parts = Split(filename, "/", true);
     173         114 :   string path_so_far = prefix;
     174         738 :   for (int i = 0; i < parts.size() - 1; i++) {
     175         624 :     path_so_far += parts[i];
     176         312 :     if (mkdir(path_so_far.c_str(), 0777) != 0) {
     177         312 :       if (errno != EEXIST) {
     178           0 :         std::cerr << filename << ": while trying to create directory "
     179           0 :                   << path_so_far << ": " << strerror(errno) << std::endl;
     180             :         return false;
     181             :       }
     182             :     }
     183             :     path_so_far += '/';
     184             :   }
     185             : 
     186         114 :   return true;
     187             : }
     188             : 
     189             : // Get the absolute path of this protoc binary.
     190          17 : bool GetProtocAbsolutePath(string* path) {
     191             : #ifdef _WIN32
     192             :   char buffer[MAX_PATH];
     193             :   int len = GetModuleFileNameA(NULL, buffer, MAX_PATH);
     194             : #elif __APPLE__
     195             :   char buffer[PATH_MAX];
     196             :   int len = 0;
     197             : 
     198             :   char dirtybuffer[PATH_MAX];
     199             :   uint32_t size = sizeof(dirtybuffer);
     200             :   if (_NSGetExecutablePath(dirtybuffer, &size) == 0) {
     201             :     realpath(dirtybuffer, buffer);
     202             :     len = strlen(buffer);
     203             :   }
     204             : #else
     205             :   char buffer[PATH_MAX];
     206          17 :   int len = readlink("/proc/self/exe", buffer, PATH_MAX);
     207             : #endif
     208          17 :   if (len > 0) {
     209          17 :     path->assign(buffer, len);
     210             :     return true;
     211             :   } else {
     212             :     return false;
     213             :   }
     214             : }
     215             : 
     216             : // Whether a path is where google/protobuf/descriptor.proto and other well-known
     217             : // type protos are installed.
     218          49 : bool IsInstalledProtoPath(const string& path) {
     219             :   // Checking the descriptor.proto file should be good enough.
     220          49 :   string file_path = path + "/google/protobuf/descriptor.proto";
     221          98 :   return access(file_path.c_str(), F_OK) != -1;
     222             : }
     223             : 
     224             : // Add the paths where google/protobuf/descritor.proto and other well-known
     225             : // type protos are installed.
     226          17 : void AddDefaultProtoPaths(vector<pair<string, string> >* paths) {
     227             :   // TODO(xiaofeng): The code currently only checks relative paths of where
     228             :   // the protoc binary is installed. We probably should make it handle more
     229             :   // cases than that.
     230             :   string path;
     231          17 :   if (!GetProtocAbsolutePath(&path)) {
     232             :     return;
     233             :   }
     234             :   // Strip the binary name.
     235          17 :   size_t pos = path.find_last_of("/\\");
     236          17 :   if (pos == string::npos || pos == 0) {
     237             :     return;
     238             :   }
     239          34 :   path = path.substr(0, pos);
     240             :   // Check the binary's directory.
     241          17 :   if (IsInstalledProtoPath(path)) {
     242           2 :     paths->push_back(pair<string, string>("", path));
     243           1 :     return;
     244             :   }
     245             :   // Check if there is an include subdirectory.
     246          32 :   if (IsInstalledProtoPath(path + "/include")) {
     247           0 :     paths->push_back(pair<string, string>("", path + "/include"));
     248           0 :     return;
     249             :   }
     250             :   // Check if the upper level directory has an "include" subdirectory.
     251          16 :   pos = path.find_last_of("/\\");
     252          16 :   if (pos == string::npos || pos == 0) {
     253             :     return;
     254             :   }
     255          32 :   path = path.substr(0, pos);
     256          32 :   if (IsInstalledProtoPath(path + "/include")) {
     257           0 :     paths->push_back(pair<string, string>("", path + "/include"));
     258           0 :     return;
     259             :   }
     260             : }
     261             : }  // namespace
     262             : 
     263             : // A MultiFileErrorCollector that prints errors to stderr.
     264             : class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
     265             :                                            public io::ErrorCollector {
     266             :  public:
     267             :   ErrorPrinter(ErrorFormat format, DiskSourceTree *tree = NULL)
     268          51 :     : format_(format), tree_(tree) {}
     269          17 :   ~ErrorPrinter() {}
     270             : 
     271             :   // implements MultiFileErrorCollector ------------------------------
     272           0 :   void AddError(const string& filename, int line, int column,
     273             :                 const string& message) {
     274             : 
     275             :     // Print full path when running under MSVS
     276             :     string dfile;
     277           0 :     if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS &&
     278           0 :         tree_ != NULL &&
     279           0 :         tree_->VirtualFileToDiskFile(filename, &dfile)) {
     280             :       std::cerr << dfile;
     281             :     } else {
     282             :       std::cerr << filename;
     283             :     }
     284             : 
     285             :     // Users typically expect 1-based line/column numbers, so we add 1
     286             :     // to each here.
     287           0 :     if (line != -1) {
     288             :       // Allow for both GCC- and Visual-Studio-compatible output.
     289           0 :       switch (format_) {
     290             :         case CommandLineInterface::ERROR_FORMAT_GCC:
     291           0 :           std::cerr << ":" << (line + 1) << ":" << (column + 1);
     292             :           break;
     293             :         case CommandLineInterface::ERROR_FORMAT_MSVS:
     294           0 :           std::cerr << "(" << (line + 1)
     295           0 :                     << ") : error in column=" << (column + 1);
     296             :           break;
     297             :       }
     298             :     }
     299             : 
     300           0 :     std::cerr << ": " << message << std::endl;
     301           0 :   }
     302             : 
     303             :   // implements io::ErrorCollector -----------------------------------
     304           0 :   void AddError(int line, int column, const string& message) {
     305           0 :     AddError("input", line, column, message);
     306           0 :   }
     307             : 
     308             :  private:
     309             :   const ErrorFormat format_;
     310             :   DiskSourceTree *tree_;
     311             : };
     312             : 
     313             : // -------------------------------------------------------------------
     314             : 
     315             : // A GeneratorContext implementation that buffers files in memory, then dumps
     316             : // them all to disk on demand.
     317             : class CommandLineInterface::GeneratorContextImpl : public GeneratorContext {
     318             :  public:
     319             :   GeneratorContextImpl(const vector<const FileDescriptor*>& parsed_files);
     320             :   ~GeneratorContextImpl();
     321             : 
     322             :   // Write all files in the directory to disk at the given output location,
     323             :   // which must end in a '/'.
     324             :   bool WriteAllToDisk(const string& prefix);
     325             : 
     326             :   // Write the contents of this directory to a ZIP-format archive with the
     327             :   // given name.
     328             :   bool WriteAllToZip(const string& filename);
     329             : 
     330             :   // Add a boilerplate META-INF/MANIFEST.MF file as required by the Java JAR
     331             :   // format, unless one has already been written.
     332             :   void AddJarManifest();
     333             : 
     334             :   // Get name of all output files.
     335             :   void GetOutputFilenames(vector<string>* output_filenames);
     336             : 
     337             :   // implements GeneratorContext --------------------------------------
     338             :   io::ZeroCopyOutputStream* Open(const string& filename);
     339             :   io::ZeroCopyOutputStream* OpenForAppend(const string& filename);
     340             :   io::ZeroCopyOutputStream* OpenForInsert(
     341             :       const string& filename, const string& insertion_point);
     342           0 :   void ListParsedFiles(vector<const FileDescriptor*>* output) {
     343           0 :     *output = parsed_files_;
     344           0 :   }
     345             : 
     346             :  private:
     347             :   friend class MemoryOutputStream;
     348             : 
     349             :   // map instead of hash_map so that files are written in order (good when
     350             :   // writing zips).
     351             :   map<string, string*> files_;
     352             :   const vector<const FileDescriptor*>& parsed_files_;
     353             :   bool had_error_;
     354             : };
     355             : 
     356             : class CommandLineInterface::MemoryOutputStream
     357             :     : public io::ZeroCopyOutputStream {
     358             :  public:
     359             :   MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
     360             :                      bool append_mode);
     361             :   MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
     362             :                      const string& insertion_point);
     363             :   virtual ~MemoryOutputStream();
     364             : 
     365             :   // implements ZeroCopyOutputStream ---------------------------------
     366        3176 :   virtual bool Next(void** data, int* size) { return inner_->Next(data, size); }
     367         228 :   virtual void BackUp(int count)            {        inner_->BackUp(count);    }
     368           0 :   virtual int64 ByteCount() const           { return inner_->ByteCount();      }
     369             : 
     370             :  private:
     371             :   // Where to insert the string when it's done.
     372             :   GeneratorContextImpl* directory_;
     373             :   string filename_;
     374             :   string insertion_point_;
     375             : 
     376             :   // The string we're building.
     377             :   string data_;
     378             : 
     379             :   // Whether we should append the output stream to the existing file.
     380             :   bool append_mode_;
     381             : 
     382             :   // StringOutputStream writing to data_.
     383             :   google::protobuf::scoped_ptr<io::StringOutputStream> inner_;
     384             : };
     385             : 
     386             : // -------------------------------------------------------------------
     387             : 
     388          17 : CommandLineInterface::GeneratorContextImpl::GeneratorContextImpl(
     389             :     const vector<const FileDescriptor*>& parsed_files)
     390             :     : parsed_files_(parsed_files),
     391          51 :       had_error_(false) {
     392          17 : }
     393             : 
     394          68 : CommandLineInterface::GeneratorContextImpl::~GeneratorContextImpl() {
     395          17 :   STLDeleteValues(&files_);
     396          34 : }
     397             : 
     398          17 : bool CommandLineInterface::GeneratorContextImpl::WriteAllToDisk(
     399             :     const string& prefix) {
     400          17 :   if (had_error_) {
     401             :     return false;
     402             :   }
     403             : 
     404          17 :   if (!VerifyDirectoryExists(prefix)) {
     405             :     return false;
     406             :   }
     407             : 
     408         182 :   for (map<string, string*>::const_iterator iter = files_.begin();
     409         262 :        iter != files_.end(); ++iter) {
     410         114 :     const string& relative_filename = iter->first;
     411         228 :     const char* data = iter->second->data();
     412         228 :     int size = iter->second->size();
     413             : 
     414         114 :     if (!TryCreateParentDirectory(prefix, relative_filename)) {
     415           0 :       return false;
     416             :     }
     417         114 :     string filename = prefix + relative_filename;
     418             : 
     419             :     // Create the output file.
     420             :     int file_descriptor;
     421         114 :     do {
     422             :       file_descriptor =
     423         228 :         open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
     424           0 :     } while (file_descriptor < 0 && errno == EINTR);
     425             : 
     426         114 :     if (file_descriptor < 0) {
     427           0 :       int error = errno;
     428           0 :       std::cerr << filename << ": " << strerror(error);
     429             :       return false;
     430             :     }
     431             : 
     432             :     // Write the file.
     433         228 :     while (size > 0) {
     434             :       int write_result;
     435         114 :       do {
     436         114 :         write_result = write(file_descriptor, data, size);
     437           0 :       } while (write_result < 0 && errno == EINTR);
     438             : 
     439         114 :       if (write_result <= 0) {
     440             :         // Write error.
     441             : 
     442             :         // FIXME(kenton):  According to the man page, if write() returns zero,
     443             :         //   there was no error; write() simply did not write anything.  It's
     444             :         //   unclear under what circumstances this might happen, but presumably
     445             :         //   errno won't be set in this case.  I am confused as to how such an
     446             :         //   event should be handled.  For now I'm treating it as an error,
     447             :         //   since retrying seems like it could lead to an infinite loop.  I
     448             :         //   suspect this never actually happens anyway.
     449             : 
     450           0 :         if (write_result < 0) {
     451           0 :           int error = errno;
     452           0 :           std::cerr << filename << ": write: " << strerror(error);
     453             :         } else {
     454           0 :           std::cerr << filename << ": write() returned zero?" << std::endl;
     455             :         }
     456             :         return false;
     457             :       }
     458             : 
     459         114 :       data += write_result;
     460         114 :       size -= write_result;
     461             :     }
     462             : 
     463         114 :     if (close(file_descriptor) != 0) {
     464           0 :       int error = errno;
     465           0 :       std::cerr << filename << ": close: " << strerror(error);
     466             :       return false;
     467             :     }
     468             :   }
     469             : 
     470             :   return true;
     471             : }
     472             : 
     473           0 : bool CommandLineInterface::GeneratorContextImpl::WriteAllToZip(
     474             :     const string& filename) {
     475           0 :   if (had_error_) {
     476             :     return false;
     477             :   }
     478             : 
     479             :   // Create the output file.
     480             :   int file_descriptor;
     481           0 :   do {
     482             :     file_descriptor =
     483           0 :       open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
     484           0 :   } while (file_descriptor < 0 && errno == EINTR);
     485             : 
     486           0 :   if (file_descriptor < 0) {
     487           0 :     int error = errno;
     488           0 :     std::cerr << filename << ": " << strerror(error);
     489             :     return false;
     490             :   }
     491             : 
     492             :   // Create the ZipWriter
     493           0 :   io::FileOutputStream stream(file_descriptor);
     494           0 :   ZipWriter zip_writer(&stream);
     495             : 
     496           0 :   for (map<string, string*>::const_iterator iter = files_.begin();
     497           0 :        iter != files_.end(); ++iter) {
     498           0 :     zip_writer.Write(iter->first, *iter->second);
     499             :   }
     500             : 
     501           0 :   zip_writer.WriteDirectory();
     502             : 
     503           0 :   if (stream.GetErrno() != 0) {
     504           0 :     std::cerr << filename << ": " << strerror(stream.GetErrno()) << std::endl;
     505             :   }
     506             : 
     507           0 :   if (!stream.Close()) {
     508           0 :     std::cerr << filename << ": " << strerror(stream.GetErrno()) << std::endl;
     509             :   }
     510             : 
     511           0 :   return true;
     512             : }
     513             : 
     514           0 : void CommandLineInterface::GeneratorContextImpl::AddJarManifest() {
     515           0 :   string** map_slot = &files_["META-INF/MANIFEST.MF"];
     516           0 :   if (*map_slot == NULL) {
     517             :     *map_slot = new string(
     518             :         "Manifest-Version: 1.0\n"
     519             :         "Created-By: 1.6.0 (protoc)\n"
     520           0 :         "\n");
     521             :   }
     522           0 : }
     523             : 
     524           0 : void CommandLineInterface::GeneratorContextImpl::GetOutputFilenames(
     525             :     vector<string>* output_filenames) {
     526           0 :   for (map<string, string*>::iterator iter = files_.begin();
     527           0 :        iter != files_.end(); ++iter) {
     528           0 :     output_filenames->push_back(iter->first);
     529             :   }
     530           0 : }
     531             : 
     532         114 : io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open(
     533             :     const string& filename) {
     534         114 :   return new MemoryOutputStream(this, filename, false);
     535             : }
     536             : 
     537             : io::ZeroCopyOutputStream*
     538           0 : CommandLineInterface::GeneratorContextImpl::OpenForAppend(
     539             :     const string& filename) {
     540           0 :   return new MemoryOutputStream(this, filename, true);
     541             : }
     542             : 
     543             : io::ZeroCopyOutputStream*
     544           0 : CommandLineInterface::GeneratorContextImpl::OpenForInsert(
     545             :     const string& filename, const string& insertion_point) {
     546           0 :   return new MemoryOutputStream(this, filename, insertion_point);
     547             : }
     548             : 
     549             : // -------------------------------------------------------------------
     550             : 
     551         114 : CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
     552             :     GeneratorContextImpl* directory, const string& filename, bool append_mode)
     553             :     : directory_(directory),
     554             :       filename_(filename),
     555             :       append_mode_(append_mode),
     556         456 :       inner_(new io::StringOutputStream(&data_)) {
     557         114 : }
     558             : 
     559           0 : CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
     560             :     GeneratorContextImpl* directory, const string& filename,
     561             :     const string& insertion_point)
     562             :     : directory_(directory),
     563             :       filename_(filename),
     564             :       insertion_point_(insertion_point),
     565           0 :       inner_(new io::StringOutputStream(&data_)) {
     566           0 : }
     567             : 
     568         798 : CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
     569             :   // Make sure all data has been written.
     570         114 :   inner_.reset();
     571             : 
     572             :   // Insert into the directory.
     573         114 :   string** map_slot = &directory_->files_[filename_];
     574             : 
     575         228 :   if (insertion_point_.empty()) {
     576             :     // This was just a regular Open().
     577         114 :     if (*map_slot != NULL) {
     578           0 :       if (append_mode_) {
     579           0 :         (*map_slot)->append(data_);
     580             :       } else {
     581           0 :         std::cerr << filename_ << ": Tried to write the same file twice."
     582             :                   << std::endl;
     583           0 :         directory_->had_error_ = true;
     584             :       }
     585             :       return;
     586             :     }
     587             : 
     588         228 :     *map_slot = new string;
     589         114 :     (*map_slot)->swap(data_);
     590             :   } else {
     591             :     // This was an OpenForInsert().
     592             : 
     593             :     // If the data doens't end with a clean line break, add one.
     594           0 :     if (!data_.empty() && data_[data_.size() - 1] != '\n') {
     595           0 :       data_.push_back('\n');
     596             :     }
     597             : 
     598             :     // Find the file we are going to insert into.
     599           0 :     if (*map_slot == NULL) {
     600           0 :       std::cerr << filename_
     601           0 :                 << ": Tried to insert into file that doesn't exist."
     602             :                 << std::endl;
     603           0 :       directory_->had_error_ = true;
     604           0 :       return;
     605             :     }
     606           0 :     string* target = *map_slot;
     607             : 
     608             :     // Find the insertion point.
     609             :     string magic_string = strings::Substitute(
     610           0 :         "@@protoc_insertion_point($0)", insertion_point_);
     611           0 :     string::size_type pos = target->find(magic_string);
     612             : 
     613           0 :     if (pos == string::npos) {
     614           0 :       std::cerr << filename_ << ": insertion point \"" << insertion_point_
     615           0 :                 << "\" not found." << std::endl;
     616           0 :       directory_->had_error_ = true;
     617             :       return;
     618             :     }
     619             : 
     620             :     // Seek backwards to the beginning of the line, which is where we will
     621             :     // insert the data.  Note that this has the effect of pushing the insertion
     622             :     // point down, so the data is inserted before it.  This is intentional
     623             :     // because it means that multiple insertions at the same point will end
     624             :     // up in the expected order in the final output.
     625           0 :     pos = target->find_last_of('\n', pos);
     626           0 :     if (pos == string::npos) {
     627             :       // Insertion point is on the first line.
     628             :       pos = 0;
     629             :     } else {
     630             :       // Advance to character after '\n'.
     631           0 :       ++pos;
     632             :     }
     633             : 
     634             :     // Extract indent.
     635           0 :     string indent_(*target, pos, target->find_first_not_of(" \t", pos) - pos);
     636             : 
     637           0 :     if (indent_.empty()) {
     638             :       // No indent.  This makes things easier.
     639           0 :       target->insert(pos, data_);
     640             :     } else {
     641             :       // Calculate how much space we need.
     642             :       int indent_size = 0;
     643           0 :       for (int i = 0; i < data_.size(); i++) {
     644           0 :         if (data_[i] == '\n') indent_size += indent_.size();
     645             :       }
     646             : 
     647             :       // Make a hole for it.
     648           0 :       target->insert(pos, data_.size() + indent_size, '\0');
     649             : 
     650             :       // Now copy in the data.
     651           0 :       string::size_type data_pos = 0;
     652           0 :       char* target_ptr = string_as_array(target) + pos;
     653           0 :       while (data_pos < data_.size()) {
     654             :         // Copy indent.
     655           0 :         memcpy(target_ptr, indent_.data(), indent_.size());
     656           0 :         target_ptr += indent_.size();
     657             : 
     658             :         // Copy line from data_.
     659             :         // We already guaranteed that data_ ends with a newline (above), so this
     660             :         // search can't fail.
     661             :         string::size_type line_length =
     662           0 :             data_.find_first_of('\n', data_pos) + 1 - data_pos;
     663           0 :         memcpy(target_ptr, data_.data() + data_pos, line_length);
     664           0 :         target_ptr += line_length;
     665           0 :         data_pos += line_length;
     666             :       }
     667             : 
     668           0 :       GOOGLE_CHECK_EQ(target_ptr,
     669           0 :           string_as_array(target) + pos + data_.size() + indent_size);
     670             :     }
     671             :   }
     672         342 : }
     673             : 
     674             : // ===================================================================
     675             : 
     676          17 : CommandLineInterface::CommandLineInterface()
     677             :   : mode_(MODE_COMPILE),
     678             :     print_mode_(PRINT_NONE),
     679             :     error_format_(ERROR_FORMAT_GCC),
     680             :     imports_in_descriptor_set_(false),
     681             :     source_info_in_descriptor_set_(false),
     682             :     disallow_services_(false),
     683         238 :     inputs_are_proto_path_relative_(false) {}
     684         187 : CommandLineInterface::~CommandLineInterface() {}
     685             : 
     686         102 : void CommandLineInterface::RegisterGenerator(const string& flag_name,
     687             :                                              CodeGenerator* generator,
     688             :                                              const string& help_text) {
     689             :   GeneratorInfo info;
     690             :   info.flag_name = flag_name;
     691         102 :   info.generator = generator;
     692             :   info.help_text = help_text;
     693         102 :   generators_by_flag_name_[flag_name] = info;
     694         102 : }
     695             : 
     696          17 : void CommandLineInterface::RegisterGenerator(const string& flag_name,
     697             :                                              const string& option_flag_name,
     698             :                                              CodeGenerator* generator,
     699             :                                              const string& help_text) {
     700             :   GeneratorInfo info;
     701             :   info.flag_name = flag_name;
     702             :   info.option_flag_name = option_flag_name;
     703          17 :   info.generator = generator;
     704             :   info.help_text = help_text;
     705          17 :   generators_by_flag_name_[flag_name] = info;
     706          17 :   generators_by_option_name_[option_flag_name] = info;
     707          17 : }
     708             : 
     709          17 : void CommandLineInterface::AllowPlugins(const string& exe_name_prefix) {
     710          17 :   plugin_prefix_ = exe_name_prefix;
     711          17 : }
     712             : 
     713          17 : int CommandLineInterface::Run(int argc, const char* const argv[]) {
     714          17 :   Clear();
     715          17 :   switch (ParseArguments(argc, argv)) {
     716             :     case PARSE_ARGUMENT_DONE_AND_EXIT:
     717             :       return 0;
     718             :     case PARSE_ARGUMENT_FAIL:
     719           0 :       return 1;
     720             :     case PARSE_ARGUMENT_DONE_AND_CONTINUE:
     721             :       break;
     722             :   }
     723             : 
     724          87 :   AddDefaultProtoPaths(&proto_path_);
     725             : 
     726             :   // Set up the source tree.
     727          17 :   DiskSourceTree source_tree;
     728         123 :   for (int i = 0; i < proto_path_.size(); i++) {
     729          36 :     source_tree.MapPath(proto_path_[i].first, proto_path_[i].second);
     730             :   }
     731             : 
     732             :   // Map input files to virtual paths if necessary.
     733          17 :   if (!inputs_are_proto_path_relative_) {
     734          17 :     if (!MakeInputsBeProtoPathRelative(&source_tree)) {
     735             :       return 1;
     736             :     }
     737             :   }
     738             : 
     739             :   // Allocate the Importer.
     740          34 :   ErrorPrinter error_collector(error_format_, &source_tree);
     741          34 :   Importer importer(&source_tree, &error_collector);
     742             : 
     743             :   vector<const FileDescriptor*> parsed_files;
     744             : 
     745             :   // Parse each file.
     746         370 :   for (int i = 0; i < input_files_.size(); i++) {
     747             :     // Import the file.
     748         319 :     importer.AddUnusedImportTrackFile(input_files_[i]);
     749         171 :     const FileDescriptor* parsed_file = importer.Import(input_files_[i]);
     750          57 :     importer.ClearUnusedImportTrackFiles();
     751          57 :     if (parsed_file == NULL) return 1;
     752          57 :     parsed_files.push_back(parsed_file);
     753             : 
     754             :     // Enforce --disallow_services.
     755          57 :     if (disallow_services_ && parsed_file->service_count() > 0) {
     756           0 :       cerr << parsed_file->name() << ": This file contains services, but "
     757           0 :               "--disallow_services was used." << endl;
     758             :       return 1;
     759             :     }
     760             :   }
     761             : 
     762             :   // We construct a separate GeneratorContext for each output location.  Note
     763             :   // that two code generators may output to the same location, in which case
     764             :   // they should share a single GeneratorContext so that OpenForInsert() works.
     765          17 :   GeneratorContextMap output_directories;
     766             : 
     767             :   // Generate output.
     768          17 :   if (mode_ == MODE_COMPILE) {
     769         119 :     for (int i = 0; i < output_directives_.size(); i++) {
     770         119 :       string output_location = output_directives_[i].output_location;
     771          51 :       if (!HasSuffixString(output_location, ".zip") &&
     772          51 :           !HasSuffixString(output_location, ".jar")) {
     773          17 :         AddTrailingSlash(&output_location);
     774             :       }
     775          17 :       GeneratorContextImpl** map_slot = &output_directories[output_location];
     776             : 
     777          17 :       if (*map_slot == NULL) {
     778             :         // First time we've seen this output location.
     779          17 :         *map_slot = new GeneratorContextImpl(parsed_files);
     780             :       }
     781             : 
     782          51 :       if (!GenerateOutput(parsed_files, output_directives_[i], *map_slot)) {
     783           0 :         STLDeleteValues(&output_directories);
     784           0 :         return 1;
     785             :       }
     786             :     }
     787             :   }
     788             : 
     789             :   // Write all output to disk.
     790          68 :   for (GeneratorContextMap::iterator iter = output_directories.begin();
     791          68 :        iter != output_directories.end(); ++iter) {
     792          34 :     const string& location = iter->first;
     793          34 :     GeneratorContextImpl* directory = iter->second;
     794          34 :     if (HasSuffixString(location, "/")) {
     795          17 :       if (!directory->WriteAllToDisk(location)) {
     796           0 :         STLDeleteValues(&output_directories);
     797             :         return 1;
     798             :       }
     799             :     } else {
     800           0 :       if (HasSuffixString(location, ".jar")) {
     801           0 :         directory->AddJarManifest();
     802             :       }
     803             : 
     804           0 :       if (!directory->WriteAllToZip(location)) {
     805           0 :         STLDeleteValues(&output_directories);
     806             :         return 1;
     807             :       }
     808             :     }
     809             :   }
     810             : 
     811          34 :   if (!dependency_out_name_.empty()) {
     812           0 :     if (!GenerateDependencyManifestFile(parsed_files, output_directories,
     813           0 :                                         &source_tree)) {
     814             :       return 1;
     815             :     }
     816             :   }
     817             : 
     818          17 :   STLDeleteValues(&output_directories);
     819             : 
     820          34 :   if (!descriptor_set_name_.empty()) {
     821           0 :     if (!WriteDescriptorSet(parsed_files)) {
     822             :       return 1;
     823             :     }
     824             :   }
     825             : 
     826          17 :   if (mode_ == MODE_ENCODE || mode_ == MODE_DECODE) {
     827           0 :     if (codec_type_.empty()) {
     828             :       // HACK:  Define an EmptyMessage type to use for decoding.
     829           0 :       DescriptorPool pool;
     830           0 :       FileDescriptorProto file;
     831           0 :       file.set_name("empty_message.proto");
     832           0 :       file.add_message_type()->set_name("EmptyMessage");
     833           0 :       GOOGLE_CHECK(pool.BuildFile(file) != NULL);
     834           0 :       codec_type_ = "EmptyMessage";
     835           0 :       if (!EncodeOrDecode(&pool)) {
     836           0 :         return 1;
     837           0 :       }
     838             :     } else {
     839           0 :       if (!EncodeOrDecode(importer.pool())) {
     840             :         return 1;
     841             :       }
     842             :     }
     843             :   }
     844             : 
     845          17 :   if (mode_ == MODE_PRINT) {
     846           0 :     switch (print_mode_) {
     847             :       case PRINT_FREE_FIELDS:
     848           0 :         for (int i = 0; i < parsed_files.size(); ++i) {
     849           0 :           const FileDescriptor* fd = parsed_files[i];
     850           0 :           for (int j = 0; j < fd->message_type_count(); ++j) {
     851           0 :             PrintFreeFieldNumbers(fd->message_type(j));
     852             :           }
     853             :         }
     854             :         break;
     855             :       case PRINT_NONE:
     856           0 :         GOOGLE_LOG(ERROR) << "If the code reaches here, it usually means a bug of "
     857           0 :                      "flag parsing in the CommonadLineInterface.";
     858           0 :         return 1;
     859             : 
     860             :       // Do not add a default case.
     861             :     }
     862             :   }
     863             : 
     864          17 :   return 0;
     865             : }
     866             : 
     867          17 : void CommandLineInterface::Clear() {
     868             :   // Clear all members that are set by Run().  Note that we must not clear
     869             :   // members which are set by other methods before Run() is called.
     870          17 :   executable_name_.clear();
     871          17 :   proto_path_.clear();
     872          17 :   input_files_.clear();
     873          17 :   output_directives_.clear();
     874          17 :   codec_type_.clear();
     875          17 :   descriptor_set_name_.clear();
     876          17 :   dependency_out_name_.clear();
     877             : 
     878          17 :   mode_ = MODE_COMPILE;
     879          17 :   print_mode_ = PRINT_NONE;
     880          17 :   imports_in_descriptor_set_ = false;
     881          17 :   source_info_in_descriptor_set_ = false;
     882          17 :   disallow_services_ = false;
     883          17 : }
     884             : 
     885          17 : bool CommandLineInterface::MakeInputsBeProtoPathRelative(
     886             :     DiskSourceTree* source_tree) {
     887         370 :   for (int i = 0; i < input_files_.size(); i++) {
     888             :     string virtual_file, shadowing_disk_file;
     889          57 :     switch (source_tree->DiskFileToVirtualFile(
     890         319 :         input_files_[i], &virtual_file, &shadowing_disk_file)) {
     891             :       case DiskSourceTree::SUCCESS:
     892         171 :         input_files_[i] = virtual_file;
     893             :         break;
     894             :       case DiskSourceTree::SHADOWED:
     895           0 :         std::cerr << input_files_[i]
     896           0 :                   << ": Input is shadowed in the --proto_path by \""
     897             :                   << shadowing_disk_file
     898             :                   << "\".  Either use the latter file as your input or reorder "
     899             :                      "the --proto_path so that the former file's location "
     900           0 :                      "comes first." << std::endl;
     901             :         return false;
     902             :       case DiskSourceTree::CANNOT_OPEN:
     903           0 :         std::cerr << input_files_[i] << ": " << strerror(errno) << std::endl;
     904             :         return false;
     905             :       case DiskSourceTree::NO_MAPPING:
     906             :         // First check if the file exists at all.
     907           0 :         if (access(input_files_[i].c_str(), F_OK) < 0) {
     908             :           // File does not even exist.
     909           0 :           std::cerr << input_files_[i] << ": " << strerror(ENOENT) << std::endl;
     910             :         } else {
     911             :           std::cerr
     912           0 :               << input_files_[i]
     913             :               << ": File does not reside within any path "
     914             :                  "specified using --proto_path (or -I).  You must specify a "
     915             :                  "--proto_path which encompasses this file.  Note that the "
     916             :                  "proto_path must be an exact prefix of the .proto file "
     917             :                  "names -- protoc is too dumb to figure out when two paths "
     918             :                  "(e.g. absolute and relative) are equivalent (it's harder "
     919           0 :                  "than you think)." << std::endl;
     920             :         }
     921             :         return false;
     922             :     }
     923             :   }
     924             : 
     925             :   return true;
     926             : }
     927             : 
     928             : CommandLineInterface::ParseArgumentStatus
     929          17 : CommandLineInterface::ParseArguments(int argc, const char* const argv[]) {
     930          17 :   executable_name_ = argv[0];
     931             : 
     932             :   // Iterate through all arguments and parse them.
     933         117 :   for (int i = 1; i < argc; i++) {
     934             :     string name, value;
     935             : 
     936          83 :     if (ParseArgument(argv[i], &name, &value)) {
     937             :       // Returned true => Use the next argument as the flag value.
     938           0 :       if (i + 1 == argc || argv[i+1][0] == '-') {
     939           0 :         std::cerr << "Missing value for flag: " << name << std::endl;
     940           0 :         if (name == "--decode") {
     941           0 :           std::cerr << "To decode an unknown message, use --decode_raw."
     942             :                     << std::endl;
     943             :         }
     944             :         return PARSE_ARGUMENT_FAIL;
     945             :       } else {
     946           0 :         ++i;
     947           0 :         value = argv[i];
     948             :       }
     949             :     }
     950             : 
     951          83 :     ParseArgumentStatus status = InterpretArgument(name, value);
     952          83 :     if (status != PARSE_ARGUMENT_DONE_AND_CONTINUE)
     953             :       return status;
     954             :   }
     955             : 
     956             :   // If no --proto_path was given, use the current working directory.
     957          34 :   if (proto_path_.empty()) {
     958             :     // Don't use make_pair as the old/default standard library on Solaris
     959             :     // doesn't support it without explicit template parameters, which are
     960             :     // incompatible with C++0x's make_pair.
     961          48 :     proto_path_.push_back(pair<string, string>("", "."));
     962             :   }
     963             : 
     964             :   // Check some errror cases.
     965          17 :   bool decoding_raw = (mode_ == MODE_DECODE) && codec_type_.empty();
     966          17 :   if (decoding_raw && !input_files_.empty()) {
     967           0 :     std::cerr << "When using --decode_raw, no input files should be given."
     968             :               << std::endl;
     969           0 :     return PARSE_ARGUMENT_FAIL;
     970          34 :   } else if (!decoding_raw && input_files_.empty()) {
     971           0 :     std::cerr << "Missing input file." << std::endl;
     972           0 :     return PARSE_ARGUMENT_FAIL;
     973             :   }
     974          34 :   if (mode_ == MODE_COMPILE && output_directives_.empty() &&
     975           0 :       descriptor_set_name_.empty()) {
     976           0 :     std::cerr << "Missing output directives." << std::endl;
     977           0 :     return PARSE_ARGUMENT_FAIL;
     978             :   }
     979          17 :   if (mode_ != MODE_COMPILE && !dependency_out_name_.empty()) {
     980           0 :     std::cerr << "Can only use --dependency_out=FILE when generating code."
     981             :               << std::endl;
     982           0 :     return PARSE_ARGUMENT_FAIL;
     983             :   }
     984          34 :   if (!dependency_out_name_.empty() && input_files_.size() > 1) {
     985             :     std::cerr
     986           0 :         << "Can only process one input file when using --dependency_out=FILE."
     987             :         << std::endl;
     988           0 :     return PARSE_ARGUMENT_FAIL;
     989             :   }
     990          17 :   if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) {
     991             :     std::cerr << "--include_imports only makes sense when combined with "
     992           0 :                  "--descriptor_set_out." << std::endl;
     993             :   }
     994          17 :   if (source_info_in_descriptor_set_ && descriptor_set_name_.empty()) {
     995             :     std::cerr << "--include_source_info only makes sense when combined with "
     996           0 :                  "--descriptor_set_out." << std::endl;
     997             :   }
     998             : 
     999             :   return PARSE_ARGUMENT_DONE_AND_CONTINUE;
    1000             : }
    1001             : 
    1002          83 : bool CommandLineInterface::ParseArgument(const char* arg,
    1003             :                                          string* name, string* value) {
    1004          83 :   bool parsed_value = false;
    1005             : 
    1006          83 :   if (arg[0] != '-') {
    1007             :     // Not a flag.
    1008             :     name->clear();
    1009          57 :     parsed_value = true;
    1010             :     *value = arg;
    1011          26 :   } else if (arg[1] == '-') {
    1012             :     // Two dashes:  Multi-character name, with '=' separating name and
    1013             :     //   value.
    1014          25 :     const char* equals_pos = strchr(arg, '=');
    1015          25 :     if (equals_pos != NULL) {
    1016          50 :       *name = string(arg, equals_pos - arg);
    1017          25 :       *value = equals_pos + 1;
    1018          25 :       parsed_value = true;
    1019             :     } else {
    1020             :       *name = arg;
    1021             :     }
    1022             :   } else {
    1023             :     // One dash:  One-character name, all subsequent characters are the
    1024             :     //   value.
    1025           1 :     if (arg[1] == '\0') {
    1026             :       // arg is just "-".  We treat this as an input file, except that at
    1027             :       // present this will just lead to a "file not found" error.
    1028             :       name->clear();
    1029             :       *value = arg;
    1030           0 :       parsed_value = true;
    1031             :     } else {
    1032           2 :       *name = string(arg, 2);
    1033           1 :       *value = arg + 2;
    1034           1 :       parsed_value = !value->empty();
    1035             :     }
    1036             :   }
    1037             : 
    1038             :   // Need to return true iff the next arg should be used as the value for this
    1039             :   // one, false otherwise.
    1040             : 
    1041          83 :   if (parsed_value) {
    1042             :     // We already parsed a value for this flag.
    1043             :     return false;
    1044             :   }
    1045             : 
    1046           0 :   if (*name == "-h" || *name == "--help" ||
    1047           0 :       *name == "--disallow_services" ||
    1048           0 :       *name == "--include_imports" ||
    1049           0 :       *name == "--include_source_info" ||
    1050           0 :       *name == "--version" ||
    1051           0 :       *name == "--decode_raw" ||
    1052             :       *name == "--print_free_field_numbers") {
    1053             :     // HACK:  These are the only flags that don't take a value.
    1054             :     //   They probably should not be hard-coded like this but for now it's
    1055             :     //   not worth doing better.
    1056             :     return false;
    1057             :   }
    1058             : 
    1059             :   // Next argument is the flag value.
    1060           0 :   return true;
    1061             : }
    1062             : 
    1063             : CommandLineInterface::ParseArgumentStatus
    1064          83 : CommandLineInterface::InterpretArgument(const string& name,
    1065             :                                         const string& value) {
    1066          83 :   if (name.empty()) {
    1067             :     // Not a flag.  Just a filename.
    1068          57 :     if (value.empty()) {
    1069             :       std::cerr
    1070             :           << "You seem to have passed an empty string as one of the "
    1071           0 :              "arguments to " << executable_name_
    1072             :           << ".  This is actually "
    1073             :              "sort of hard to do.  Congrats.  Unfortunately it is not valid "
    1074           0 :              "input so the program is going to die now." << std::endl;
    1075           0 :       return PARSE_ARGUMENT_FAIL;
    1076             :     }
    1077             : 
    1078          57 :     input_files_.push_back(value);
    1079             : 
    1080          51 :   } else if (name == "-I" || name == "--proto_path") {
    1081             :     // Java's -classpath (and some other languages) delimits path components
    1082             :     // with colons.  Let's accept that syntax too just to make things more
    1083             :     // intuitive.
    1084             :     vector<string> parts = Split(
    1085           1 :         value, kPathSeparator, true);
    1086             : 
    1087           3 :     for (int i = 0; i < parts.size(); i++) {
    1088             :       string virtual_path;
    1089             :       string disk_path;
    1090             : 
    1091           3 :       string::size_type equals_pos = parts[i].find_first_of('=');
    1092           1 :       if (equals_pos == string::npos) {
    1093             :         virtual_path = "";
    1094           2 :         disk_path = parts[i];
    1095             :       } else {
    1096           0 :         virtual_path = parts[i].substr(0, equals_pos);
    1097           0 :         disk_path = parts[i].substr(equals_pos + 1);
    1098             :       }
    1099             : 
    1100           1 :       if (disk_path.empty()) {
    1101             :         std::cerr
    1102             :             << "--proto_path passed empty directory name.  (Use \".\" for "
    1103           0 :                "current directory.)" << std::endl;
    1104           0 :         return PARSE_ARGUMENT_FAIL;
    1105             :       }
    1106             : 
    1107             :       // Make sure disk path exists, warn otherwise.
    1108           1 :       if (access(disk_path.c_str(), F_OK) < 0) {
    1109           0 :         std::cerr << disk_path << ": warning: directory does not exist."
    1110             :                   << std::endl;
    1111             :       }
    1112             : 
    1113             :       // Don't use make_pair as the old/default standard library on Solaris
    1114             :       // doesn't support it without explicit template parameters, which are
    1115             :       // incompatible with C++0x's make_pair.
    1116           1 :       proto_path_.push_back(pair<string, string>(virtual_path, disk_path));
    1117           1 :     }
    1118             : 
    1119          50 :   } else if (name == "-o" || name == "--descriptor_set_out") {
    1120           0 :     if (!descriptor_set_name_.empty()) {
    1121           0 :       std::cerr << name << " may only be passed once." << std::endl;
    1122           0 :       return PARSE_ARGUMENT_FAIL;
    1123             :     }
    1124           0 :     if (value.empty()) {
    1125           0 :       std::cerr << name << " requires a non-empty value." << std::endl;
    1126           0 :       return PARSE_ARGUMENT_FAIL;
    1127             :     }
    1128           0 :     if (mode_ != MODE_COMPILE) {
    1129             :       std::cerr
    1130             :           << "Cannot use --encode or --decode and generate descriptors at the "
    1131           0 :              "same time." << std::endl;
    1132           0 :       return PARSE_ARGUMENT_FAIL;
    1133             :     }
    1134           0 :     descriptor_set_name_ = value;
    1135             : 
    1136          25 :   } else if (name == "--dependency_out") {
    1137           0 :     if (!dependency_out_name_.empty()) {
    1138           0 :       std::cerr << name << " may only be passed once." << std::endl;
    1139           0 :       return PARSE_ARGUMENT_FAIL;
    1140             :     }
    1141           0 :     if (value.empty()) {
    1142           0 :       std::cerr << name << " requires a non-empty value." << std::endl;
    1143           0 :       return PARSE_ARGUMENT_FAIL;
    1144             :     }
    1145           0 :     dependency_out_name_ = value;
    1146             : 
    1147          25 :   } else if (name == "--include_imports") {
    1148           0 :     if (imports_in_descriptor_set_) {
    1149           0 :       std::cerr << name << " may only be passed once." << std::endl;
    1150           0 :       return PARSE_ARGUMENT_FAIL;
    1151             :     }
    1152           0 :     imports_in_descriptor_set_ = true;
    1153             : 
    1154          25 :   } else if (name == "--include_source_info") {
    1155           0 :     if (source_info_in_descriptor_set_) {
    1156           0 :       std::cerr << name << " may only be passed once." << std::endl;
    1157           0 :       return PARSE_ARGUMENT_FAIL;
    1158             :     }
    1159           0 :     source_info_in_descriptor_set_ = true;
    1160             : 
    1161          50 :   } else if (name == "-h" || name == "--help") {
    1162           0 :     PrintHelpText();
    1163           0 :     return PARSE_ARGUMENT_DONE_AND_EXIT;  // Exit without running compiler.
    1164             : 
    1165          25 :   } else if (name == "--version") {
    1166           0 :     if (!version_info_.empty()) {
    1167           0 :       std::cout << version_info_ << std::endl;
    1168             :     }
    1169           0 :     cout << "libprotoc "
    1170           0 :          << protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION)
    1171             :          << endl;
    1172           0 :     return PARSE_ARGUMENT_DONE_AND_EXIT;  // Exit without running compiler.
    1173             : 
    1174          25 :   } else if (name == "--disallow_services") {
    1175           0 :     disallow_services_ = true;
    1176             : 
    1177          75 :   } else if (name == "--encode" || name == "--decode" ||
    1178             :              name == "--decode_raw") {
    1179           0 :     if (mode_ != MODE_COMPILE) {
    1180           0 :       std::cerr << "Only one of --encode and --decode can be specified."
    1181             :                 << std::endl;
    1182           0 :       return PARSE_ARGUMENT_FAIL;
    1183             :     }
    1184           0 :     if (!output_directives_.empty() || !descriptor_set_name_.empty()) {
    1185           0 :       std::cerr << "Cannot use " << name
    1186           0 :                 << " and generate code or descriptors at the same time."
    1187             :                 << std::endl;
    1188           0 :       return PARSE_ARGUMENT_FAIL;
    1189             :     }
    1190             : 
    1191           0 :     mode_ = (name == "--encode") ? MODE_ENCODE : MODE_DECODE;
    1192             : 
    1193           0 :     if (value.empty() && name != "--decode_raw") {
    1194           0 :       std::cerr << "Type name for " << name << " cannot be blank." << std::endl;
    1195           0 :       if (name == "--decode") {
    1196           0 :         std::cerr << "To decode an unknown message, use --decode_raw."
    1197             :                   << std::endl;
    1198             :       }
    1199             :       return PARSE_ARGUMENT_FAIL;
    1200           0 :     } else if (!value.empty() && name == "--decode_raw") {
    1201           0 :       std::cerr << "--decode_raw does not take a parameter." << std::endl;
    1202           0 :       return PARSE_ARGUMENT_FAIL;
    1203             :     }
    1204             : 
    1205           0 :     codec_type_ = value;
    1206             : 
    1207          25 :   } else if (name == "--error_format") {
    1208           0 :     if (value == "gcc") {
    1209           0 :       error_format_ = ERROR_FORMAT_GCC;
    1210           0 :     } else if (value == "msvs") {
    1211           0 :       error_format_ = ERROR_FORMAT_MSVS;
    1212             :     } else {
    1213           0 :       std::cerr << "Unknown error format: " << value << std::endl;
    1214           0 :       return PARSE_ARGUMENT_FAIL;
    1215             :     }
    1216             : 
    1217          25 :   } else if (name == "--plugin") {
    1218          16 :     if (plugin_prefix_.empty()) {
    1219           0 :       std::cerr << "This compiler does not support plugins." << std::endl;
    1220           0 :       return PARSE_ARGUMENT_FAIL;
    1221             :     }
    1222             : 
    1223             :     string plugin_name;
    1224             :     string path;
    1225             : 
    1226           8 :     string::size_type equals_pos = value.find_first_of('=');
    1227           8 :     if (equals_pos == string::npos) {
    1228             :       // Use the basename of the file.
    1229           0 :       string::size_type slash_pos = value.find_last_of('/');
    1230           0 :       if (slash_pos == string::npos) {
    1231             :         plugin_name = value;
    1232             :       } else {
    1233           0 :         plugin_name = value.substr(slash_pos + 1);
    1234             :       }
    1235             :       path = value;
    1236             :     } else {
    1237          16 :       plugin_name = value.substr(0, equals_pos);
    1238          16 :       path = value.substr(equals_pos + 1);
    1239             :     }
    1240             : 
    1241           8 :     plugins_[plugin_name] = path;
    1242             : 
    1243          17 :   } else if (name == "--print_free_field_numbers") {
    1244           0 :     if (mode_ != MODE_COMPILE) {
    1245           0 :       std::cerr << "Cannot use " << name
    1246           0 :                 << " and use --encode, --decode or print "
    1247           0 :                 << "other info at the same time." << std::endl;
    1248           0 :       return PARSE_ARGUMENT_FAIL;
    1249             :     }
    1250           0 :     if (!output_directives_.empty() || !descriptor_set_name_.empty()) {
    1251           0 :       std::cerr << "Cannot use " << name
    1252           0 :                 << " and generate code or descriptors at the same time."
    1253             :                 << std::endl;
    1254           0 :       return PARSE_ARGUMENT_FAIL;
    1255             :     }
    1256           0 :     mode_ = MODE_PRINT;
    1257           0 :     print_mode_ = PRINT_FREE_FIELDS;
    1258             :   } else {
    1259             :     // Some other flag.  Look it up in the generators list.
    1260             :     const GeneratorInfo* generator_info =
    1261          34 :         FindOrNull(generators_by_flag_name_, name);
    1262          51 :     if (generator_info == NULL &&
    1263          32 :         (plugin_prefix_.empty() || !HasSuffixString(name, "_out"))) {
    1264             :       // Check if it's a generator option flag.
    1265           0 :       generator_info = FindOrNull(generators_by_option_name_, name);
    1266           0 :       if (generator_info == NULL) {
    1267           0 :         std::cerr << "Unknown flag: " << name << std::endl;
    1268           0 :         return PARSE_ARGUMENT_FAIL;
    1269             :       } else {
    1270           0 :         string* parameters = &generator_parameters_[generator_info->flag_name];
    1271           0 :         if (!parameters->empty()) {
    1272           0 :           parameters->append(",");
    1273             :         }
    1274           0 :         parameters->append(value);
    1275             :       }
    1276             :     } else {
    1277             :       // It's an output flag.  Add it to the output directives.
    1278          17 :       if (mode_ != MODE_COMPILE) {
    1279             :         std::cerr << "Cannot use --encode, --decode or print .proto info and "
    1280           0 :                      "generate code at the same time." << std::endl;
    1281           0 :         return PARSE_ARGUMENT_FAIL;
    1282             :       }
    1283             : 
    1284             :       OutputDirective directive;
    1285             :       directive.name = name;
    1286          17 :       if (generator_info == NULL) {
    1287           8 :         directive.generator = NULL;
    1288             :       } else {
    1289           9 :         directive.generator = generator_info->generator;
    1290             :       }
    1291             : 
    1292             :       // Split value at ':' to separate the generator parameter from the
    1293             :       // filename.  However, avoid doing this if the colon is part of a valid
    1294             :       // Windows-style absolute path.
    1295          17 :       string::size_type colon_pos = value.find_first_of(':');
    1296          17 :       if (colon_pos == string::npos || IsWindowsAbsolutePath(value)) {
    1297             :         directive.output_location = value;
    1298             :       } else {
    1299           0 :         directive.parameter = value.substr(0, colon_pos);
    1300           0 :         directive.output_location = value.substr(colon_pos + 1);
    1301             :       }
    1302             : 
    1303          17 :       output_directives_.push_back(directive);
    1304             :     }
    1305             :   }
    1306             : 
    1307             :   return PARSE_ARGUMENT_DONE_AND_CONTINUE;
    1308             : }
    1309             : 
    1310           0 : void CommandLineInterface::PrintHelpText() {
    1311             :   // Sorry for indentation here; line wrapping would be uglier.
    1312             :   std::cerr <<
    1313           0 : "Usage: " << executable_name_ << " [OPTION] PROTO_FILES\n"
    1314             : "Parse PROTO_FILES and generate output based on the options given:\n"
    1315             : "  -IPATH, --proto_path=PATH   Specify the directory in which to search for\n"
    1316             : "                              imports.  May be specified multiple times;\n"
    1317             : "                              directories will be searched in order.  If not\n"
    1318             : "                              given, the current working directory is used.\n"
    1319             : "  --version                   Show version info and exit.\n"
    1320             : "  -h, --help                  Show this text and exit.\n"
    1321             : "  --encode=MESSAGE_TYPE       Read a text-format message of the given type\n"
    1322             : "                              from standard input and write it in binary\n"
    1323             : "                              to standard output.  The message type must\n"
    1324             : "                              be defined in PROTO_FILES or their imports.\n"
    1325             : "  --decode=MESSAGE_TYPE       Read a binary message of the given type from\n"
    1326             : "                              standard input and write it in text format\n"
    1327             : "                              to standard output.  The message type must\n"
    1328             : "                              be defined in PROTO_FILES or their imports.\n"
    1329             : "  --decode_raw                Read an arbitrary protocol message from\n"
    1330             : "                              standard input and write the raw tag/value\n"
    1331             : "                              pairs in text format to standard output.  No\n"
    1332             : "                              PROTO_FILES should be given when using this\n"
    1333             : "                              flag.\n"
    1334             : "  -oFILE,                     Writes a FileDescriptorSet (a protocol buffer,\n"
    1335             : "    --descriptor_set_out=FILE defined in descriptor.proto) containing all of\n"
    1336             : "                              the input files to FILE.\n"
    1337             : "  --include_imports           When using --descriptor_set_out, also include\n"
    1338             : "                              all dependencies of the input files in the\n"
    1339             : "                              set, so that the set is self-contained.\n"
    1340             : "  --include_source_info       When using --descriptor_set_out, do not strip\n"
    1341             : "                              SourceCodeInfo from the FileDescriptorProto.\n"
    1342             : "                              This results in vastly larger descriptors that\n"
    1343             : "                              include information about the original\n"
    1344             : "                              location of each decl in the source file as\n"
    1345             : "                              well as surrounding comments.\n"
    1346             : "  --dependency_out=FILE       Write a dependency output file in the format\n"
    1347             : "                              expected by make. This writes the transitive\n"
    1348             : "                              set of input file paths to FILE\n"
    1349             : "  --error_format=FORMAT       Set the format in which to print errors.\n"
    1350             : "                              FORMAT may be 'gcc' (the default) or 'msvs'\n"
    1351             : "                              (Microsoft Visual Studio format).\n"
    1352             : "  --print_free_field_numbers  Print the free field numbers of the messages\n"
    1353             : "                              defined in the given proto files. Groups share\n"
    1354             : "                              the same field number space with the parent \n"
    1355             : "                              message. Extension ranges are counted as \n"
    1356           0 : "                              occupied fields numbers."
    1357             :       << std::endl;
    1358           0 :   if (!plugin_prefix_.empty()) {
    1359             :     std::cerr <<
    1360             : "  --plugin=EXECUTABLE         Specifies a plugin executable to use.\n"
    1361             : "                              Normally, protoc searches the PATH for\n"
    1362             : "                              plugins, but you may specify additional\n"
    1363             : "                              executables not in the path using this flag.\n"
    1364             : "                              Additionally, EXECUTABLE may be of the form\n"
    1365             : "                              NAME=PATH, in which case the given plugin name\n"
    1366             : "                              is mapped to the given executable even if\n"
    1367           0 : "                              the executable's own name differs." << std::endl;
    1368             :   }
    1369             : 
    1370           0 :   for (GeneratorMap::iterator iter = generators_by_flag_name_.begin();
    1371           0 :        iter != generators_by_flag_name_.end(); ++iter) {
    1372             :     // FIXME(kenton):  If the text is long enough it will wrap, which is ugly,
    1373             :     //   but fixing this nicely (e.g. splitting on spaces) is probably more
    1374             :     //   trouble than it's worth.
    1375           0 :     std::cerr << "  " << iter->first << "=OUT_DIR "
    1376           0 :               << string(19 - iter->first.size(), ' ')  // Spaces for alignment.
    1377           0 :               << iter->second.help_text << std::endl;
    1378             :   }
    1379           0 : }
    1380             : 
    1381          17 : bool CommandLineInterface::GenerateOutput(
    1382         116 :     const vector<const FileDescriptor*>& parsed_files,
    1383             :     const OutputDirective& output_directive,
    1384             :     GeneratorContext* generator_context) {
    1385             :   // Call the generator.
    1386             :   string error;
    1387          17 :   if (output_directive.generator == NULL) {
    1388             :     // This is a plugin.
    1389          32 :     GOOGLE_CHECK(HasPrefixString(output_directive.name, "--") &&
    1390             :           HasSuffixString(output_directive.name, "_out"))
    1391          16 :         << "Bad name for plugin generator: " << output_directive.name;
    1392             : 
    1393             :     // Strip the "--" and "_out" and add the plugin prefix.
    1394          16 :     string plugin_name = plugin_prefix_ + "gen-" +
    1395          32 :         output_directive.name.substr(2, output_directive.name.size() - 6);
    1396             : 
    1397           8 :     if (!GeneratePluginOutput(parsed_files, plugin_name,
    1398             :                               output_directive.parameter,
    1399           8 :                               generator_context, &error)) {
    1400           0 :       std::cerr << output_directive.name << ": " << error << std::endl;
    1401           0 :       return false;
    1402             :     }
    1403             :   } else {
    1404             :     // Regular generator.
    1405           9 :     string parameters = output_directive.parameter;
    1406          18 :     if (!generator_parameters_[output_directive.name].empty()) {
    1407           0 :       if (!parameters.empty()) {
    1408           0 :         parameters.append(",");
    1409             :       }
    1410           0 :       parameters.append(generator_parameters_[output_directive.name]);
    1411             :     }
    1412           9 :     if (output_directive.generator->HasGenerateAll()) {
    1413           0 :       if (!output_directive.generator->GenerateAll(
    1414           0 :           parsed_files, parameters, generator_context, &error)) {
    1415             :           // Generator returned an error.
    1416           0 :           std::cerr << output_directive.name << ": "
    1417           0 :                     << ": " << error << std::endl;
    1418             :           return false;
    1419             :       }
    1420             :     } else {
    1421         223 :       for (int i = 0; i < parsed_files.size(); i++) {
    1422          98 :         if (!output_directive.generator->Generate(parsed_files[i], parameters,
    1423          98 :                                                   generator_context, &error)) {
    1424             :           // Generator returned an error.
    1425           0 :           std::cerr << output_directive.name << ": " << parsed_files[i]->name()
    1426           0 :                     << ": " << error << std::endl;
    1427             :           return false;
    1428             :         }
    1429             :       }
    1430             :     }
    1431             :   }
    1432             : 
    1433             :   return true;
    1434             : }
    1435             : 
    1436           0 : bool CommandLineInterface::GenerateDependencyManifestFile(
    1437           0 :     const vector<const FileDescriptor*>& parsed_files,
    1438             :     const GeneratorContextMap& output_directories,
    1439             :     DiskSourceTree* source_tree) {
    1440           0 :   FileDescriptorSet file_set;
    1441             : 
    1442             :   set<const FileDescriptor*> already_seen;
    1443           0 :   for (int i = 0; i < parsed_files.size(); i++) {
    1444           0 :     GetTransitiveDependencies(parsed_files[i],
    1445             :                               false,
    1446             :                               &already_seen,
    1447           0 :                               file_set.mutable_file());
    1448             :   }
    1449             : 
    1450           0 :   vector<string> output_filenames;
    1451           0 :   for (GeneratorContextMap::const_iterator iter = output_directories.begin();
    1452           0 :        iter != output_directories.end(); ++iter) {
    1453           0 :     const string& location = iter->first;
    1454           0 :     GeneratorContextImpl* directory = iter->second;
    1455             :     vector<string> relative_output_filenames;
    1456           0 :     directory->GetOutputFilenames(&relative_output_filenames);
    1457           0 :     for (int i = 0; i < relative_output_filenames.size(); i++) {
    1458           0 :       string output_filename = location + relative_output_filenames[i];
    1459           0 :       if (output_filename.compare(0, 2, "./") == 0) {
    1460           0 :         output_filename = output_filename.substr(2);
    1461             :       }
    1462           0 :       output_filenames.push_back(output_filename);
    1463             :     }
    1464           0 :   }
    1465             : 
    1466             :   int fd;
    1467           0 :   do {
    1468             :     fd = open(dependency_out_name_.c_str(),
    1469           0 :               O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
    1470           0 :   } while (fd < 0 && errno == EINTR);
    1471             : 
    1472           0 :   if (fd < 0) {
    1473           0 :     perror(dependency_out_name_.c_str());
    1474             :     return false;
    1475             :   }
    1476             : 
    1477           0 :   io::FileOutputStream out(fd);
    1478           0 :   io::Printer printer(&out, '$');
    1479             : 
    1480           0 :   for (int i = 0; i < output_filenames.size(); i++) {
    1481           0 :     printer.Print(output_filenames[i].c_str());
    1482           0 :     if (i == output_filenames.size() - 1) {
    1483           0 :       printer.Print(":");
    1484             :     } else {
    1485           0 :       printer.Print(" \\\n");
    1486             :     }
    1487             :   }
    1488             : 
    1489           0 :   for (int i = 0; i < file_set.file_size(); i++) {
    1490           0 :     const FileDescriptorProto& file = file_set.file(i);
    1491           0 :     const string& virtual_file = file.name();
    1492             :     string disk_file;
    1493           0 :     if (source_tree &&
    1494           0 :         source_tree->VirtualFileToDiskFile(virtual_file, &disk_file)) {
    1495           0 :       printer.Print(" $disk_file$", "disk_file", disk_file);
    1496           0 :       if (i < file_set.file_size() - 1) printer.Print("\\\n");
    1497             :     } else {
    1498           0 :       std::cerr << "Unable to identify path for file " << virtual_file
    1499             :                 << std::endl;
    1500           0 :       return false;
    1501             :     }
    1502             :   }
    1503             : 
    1504           0 :   return true;
    1505             : }
    1506             : 
    1507           8 : bool CommandLineInterface::GeneratePluginOutput(
    1508          40 :     const vector<const FileDescriptor*>& parsed_files,
    1509             :     const string& plugin_name,
    1510             :     const string& parameter,
    1511             :     GeneratorContext* generator_context,
    1512             :     string* error) {
    1513           8 :   CodeGeneratorRequest request;
    1514          16 :   CodeGeneratorResponse response;
    1515             : 
    1516             :   // Build the request.
    1517           8 :   if (!parameter.empty()) {
    1518             :     request.set_parameter(parameter);
    1519             :   }
    1520             : 
    1521             :   set<const FileDescriptor*> already_seen;
    1522          64 :   for (int i = 0; i < parsed_files.size(); i++) {
    1523          16 :     request.add_file_to_generate(parsed_files[i]->name());
    1524           8 :     GetTransitiveDependencies(parsed_files[i],
    1525             :                               true,  // Include source code info.
    1526           8 :                               &already_seen, request.mutable_proto_file());
    1527             :   }
    1528             : 
    1529             :   // Invoke the plugin.
    1530          16 :   Subprocess subprocess;
    1531             : 
    1532          16 :   if (plugins_.count(plugin_name) > 0) {
    1533           8 :     subprocess.Start(plugins_[plugin_name], Subprocess::EXACT_NAME);
    1534             :   } else {
    1535           0 :     subprocess.Start(plugin_name, Subprocess::SEARCH_PATH);
    1536             :   }
    1537             : 
    1538             :   string communicate_error;
    1539           8 :   if (!subprocess.Communicate(request, &response, &communicate_error)) {
    1540           0 :     *error = strings::Substitute("$0: $1", plugin_name, communicate_error);
    1541           0 :     return false;
    1542             :   }
    1543             : 
    1544             :   // Write the files.  We do this even if there was a generator error in order
    1545             :   // to match the behavior of a compiled-in generator.
    1546             :   google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> current_output;
    1547          40 :   for (int i = 0; i < response.file_size(); i++) {
    1548          16 :     const CodeGeneratorResponse::File& output_file = response.file(i);
    1549             : 
    1550          32 :     if (!output_file.insertion_point().empty()) {
    1551             :       // Open a file for insert.
    1552             :       // We reset current_output to NULL first so that the old file is closed
    1553             :       // before the new one is opened.
    1554             :       current_output.reset();
    1555             :       current_output.reset(generator_context->OpenForInsert(
    1556           0 :           output_file.name(), output_file.insertion_point()));
    1557          32 :     } else if (!output_file.name().empty()) {
    1558             :       // Starting a new file.  Open it.
    1559             :       // We reset current_output to NULL first so that the old file is closed
    1560             :       // before the new one is opened.
    1561             :       current_output.reset();
    1562          32 :       current_output.reset(generator_context->Open(output_file.name()));
    1563           0 :     } else if (current_output == NULL) {
    1564           0 :       *error = strings::Substitute(
    1565             :         "$0: First file chunk returned by plugin did not specify a file name.",
    1566             :         plugin_name);
    1567           0 :       return false;
    1568             :     }
    1569             : 
    1570             :     // Use CodedOutputStream for convenience; otherwise we'd need to provide
    1571             :     // our own buffer-copying loop.
    1572          16 :     io::CodedOutputStream writer(current_output.get());
    1573          16 :     writer.WriteString(output_file.content());
    1574          16 :   }
    1575             : 
    1576             :   // Check for errors.
    1577          16 :   if (!response.error().empty()) {
    1578             :     // Generator returned an error.
    1579           0 :     *error = response.error();
    1580             :     return false;
    1581             :   }
    1582             : 
    1583           8 :   return true;
    1584             : }
    1585             : 
    1586           0 : bool CommandLineInterface::EncodeOrDecode(const DescriptorPool* pool) {
    1587             :   // Look up the type.
    1588           0 :   const Descriptor* type = pool->FindMessageTypeByName(codec_type_);
    1589           0 :   if (type == NULL) {
    1590           0 :     std::cerr << "Type not defined: " << codec_type_ << std::endl;
    1591             :     return false;
    1592             :   }
    1593             : 
    1594           0 :   DynamicMessageFactory dynamic_factory(pool);
    1595           0 :   google::protobuf::scoped_ptr<Message> message(dynamic_factory.GetPrototype(type)->New());
    1596             : 
    1597             :   if (mode_ == MODE_ENCODE) {
    1598             :     SetFdToTextMode(STDIN_FILENO);
    1599             :     SetFdToBinaryMode(STDOUT_FILENO);
    1600             :   } else {
    1601             :     SetFdToBinaryMode(STDIN_FILENO);
    1602             :     SetFdToTextMode(STDOUT_FILENO);
    1603             :   }
    1604             : 
    1605           0 :   io::FileInputStream in(STDIN_FILENO);
    1606           0 :   io::FileOutputStream out(STDOUT_FILENO);
    1607             : 
    1608           0 :   if (mode_ == MODE_ENCODE) {
    1609             :     // Input is text.
    1610           0 :     ErrorPrinter error_collector(error_format_);
    1611           0 :     TextFormat::Parser parser;
    1612           0 :     parser.RecordErrorsTo(&error_collector);
    1613           0 :     parser.AllowPartialMessage(true);
    1614             : 
    1615           0 :     if (!parser.Parse(&in, message.get())) {
    1616           0 :       std::cerr << "Failed to parse input." << std::endl;
    1617           0 :       return false;
    1618           0 :     }
    1619             :   } else {
    1620             :     // Input is binary.
    1621           0 :     if (!message->ParsePartialFromZeroCopyStream(&in)) {
    1622           0 :       std::cerr << "Failed to parse input." << std::endl;
    1623             :       return false;
    1624             :     }
    1625             :   }
    1626             : 
    1627           0 :   if (!message->IsInitialized()) {
    1628           0 :     std::cerr << "warning:  Input message is missing required fields:  "
    1629           0 :               << message->InitializationErrorString() << std::endl;
    1630             :   }
    1631             : 
    1632           0 :   if (mode_ == MODE_ENCODE) {
    1633             :     // Output is binary.
    1634           0 :     if (!message->SerializePartialToZeroCopyStream(&out)) {
    1635           0 :       std::cerr << "output: I/O error." << std::endl;
    1636             :       return false;
    1637             :     }
    1638             :   } else {
    1639             :     // Output is text.
    1640           0 :     if (!TextFormat::Print(*message, &out)) {
    1641           0 :       std::cerr << "output: I/O error." << std::endl;
    1642             :       return false;
    1643             :     }
    1644             :   }
    1645             : 
    1646           0 :   return true;
    1647             : }
    1648             : 
    1649           0 : bool CommandLineInterface::WriteDescriptorSet(
    1650           0 :     const vector<const FileDescriptor*> parsed_files) {
    1651           0 :   FileDescriptorSet file_set;
    1652             : 
    1653           0 :   if (imports_in_descriptor_set_) {
    1654             :     set<const FileDescriptor*> already_seen;
    1655           0 :     for (int i = 0; i < parsed_files.size(); i++) {
    1656           0 :       GetTransitiveDependencies(parsed_files[i],
    1657             :                                 source_info_in_descriptor_set_,
    1658           0 :                                 &already_seen, file_set.mutable_file());
    1659             :     }
    1660             :   } else {
    1661             :     set<const FileDescriptor*> already_seen;
    1662           0 :     for (int i = 0; i < parsed_files.size(); i++) {
    1663           0 :       if (!already_seen.insert(parsed_files[i]).second) {
    1664             :         continue;
    1665             :       }
    1666           0 :       FileDescriptorProto* file_proto = file_set.add_file();
    1667           0 :       parsed_files[i]->CopyTo(file_proto);
    1668           0 :       if (source_info_in_descriptor_set_) {
    1669           0 :         parsed_files[i]->CopySourceCodeInfoTo(file_proto);
    1670             :       }
    1671             :     }
    1672             :   }
    1673             : 
    1674             :   int fd;
    1675           0 :   do {
    1676             :     fd = open(descriptor_set_name_.c_str(),
    1677           0 :               O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
    1678           0 :   } while (fd < 0 && errno == EINTR);
    1679             : 
    1680           0 :   if (fd < 0) {
    1681           0 :     perror(descriptor_set_name_.c_str());
    1682             :     return false;
    1683             :   }
    1684             : 
    1685           0 :   io::FileOutputStream out(fd);
    1686           0 :   if (!file_set.SerializeToZeroCopyStream(&out)) {
    1687           0 :     std::cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno())
    1688             :               << std::endl;
    1689           0 :     out.Close();
    1690             :     return false;
    1691             :   }
    1692           0 :   if (!out.Close()) {
    1693           0 :     std::cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno())
    1694             :               << std::endl;
    1695             :     return false;
    1696             :   }
    1697             : 
    1698           0 :   return true;
    1699             : }
    1700             : 
    1701          13 : void CommandLineInterface::GetTransitiveDependencies(
    1702             :     const FileDescriptor* file, bool include_source_code_info,
    1703             :     set<const FileDescriptor*>* already_seen,
    1704             :     RepeatedPtrField<FileDescriptorProto>* output) {
    1705          13 :   if (!already_seen->insert(file).second) {
    1706             :     // Already saw this file.  Skip.
    1707          13 :     return;
    1708             :   }
    1709             : 
    1710             :   // Add all dependencies.
    1711          23 :   for (int i = 0; i < file->dependency_count(); i++) {
    1712             :     GetTransitiveDependencies(file->dependency(i), include_source_code_info,
    1713          28 :                               already_seen, output);
    1714             :   }
    1715             : 
    1716             :   // Add this file.
    1717          13 :   FileDescriptorProto* new_descriptor = output->Add();
    1718          13 :   file->CopyTo(new_descriptor);
    1719          13 :   if (include_source_code_info) {
    1720          13 :     file->CopySourceCodeInfoTo(new_descriptor);
    1721             :   }
    1722             : }
    1723             : 
    1724             : namespace {
    1725             : 
    1726             : // Utility function for PrintFreeFieldNumbers.
    1727             : // Stores occupied ranges into the ranges parameter, and next level of sub
    1728             : // message types into the nested_messages parameter.  The FieldRange is left
    1729             : // inclusive, right exclusive. i.e. [a, b).
    1730             : //
    1731             : // Nested Messages:
    1732             : // Note that it only stores the nested message type, iff the nested type is
    1733             : // either a direct child of the given descriptor, or the nested type is a
    1734             : // decendent of the given descriptor and all the nodes between the
    1735             : // nested type and the given descriptor are group types. e.g.
    1736             : //
    1737             : // message Foo {
    1738             : //   message Bar {
    1739             : //     message NestedBar {}
    1740             : //   }
    1741             : //   group Baz = 1 {
    1742             : //     group NestedBazGroup = 2 {
    1743             : //       message Quz {
    1744             : //         message NestedQuz {}
    1745             : //       }
    1746             : //     }
    1747             : //     message NestedBaz {}
    1748             : //   }
    1749             : // }
    1750             : //
    1751             : // In this case, Bar, Quz and NestedBaz will be added into the nested types.
    1752             : // Since free field numbers of group types will not be printed, this makes sure
    1753             : // the nested message types in groups will not be dropped. The nested_messages
    1754             : // parameter will contain the direct children (when groups are ignored in the
    1755             : // tree) of the given descriptor for the caller to traverse. The declaration
    1756             : // order of the nested messages is also preserved.
    1757             : typedef pair<int, int> FieldRange;
    1758           0 : void GatherOccupiedFieldRanges(const Descriptor* descriptor,
    1759             :                                set<FieldRange>* ranges,
    1760             :                                vector<const Descriptor*>* nested_messages) {
    1761             :   set<const Descriptor*> groups;
    1762           0 :   for (int i = 0; i < descriptor->field_count(); ++i) {
    1763           0 :     const FieldDescriptor* fd = descriptor->field(i);
    1764           0 :     ranges->insert(FieldRange(fd->number(), fd->number() + 1));
    1765           0 :     if (fd->type() == FieldDescriptor::TYPE_GROUP) {
    1766           0 :       groups.insert(fd->message_type());
    1767             :     }
    1768             :   }
    1769           0 :   for (int i = 0; i < descriptor->extension_range_count(); ++i) {
    1770           0 :     ranges->insert(FieldRange(descriptor->extension_range(i)->start,
    1771           0 :                               descriptor->extension_range(i)->end));
    1772             :   }
    1773           0 :   for (int i = 0; i < descriptor->reserved_range_count(); ++i) {
    1774           0 :     ranges->insert(FieldRange(descriptor->reserved_range(i)->start,
    1775           0 :                               descriptor->reserved_range(i)->end));
    1776             :   }
    1777             :   // Handle the nested messages/groups in declaration order to make it
    1778             :   // post-order strict.
    1779           0 :   for (int i = 0; i < descriptor->nested_type_count(); ++i) {
    1780           0 :     const Descriptor* nested_desc = descriptor->nested_type(i);
    1781           0 :     if (groups.find(nested_desc) != groups.end()) {
    1782           0 :       GatherOccupiedFieldRanges(nested_desc, ranges, nested_messages);
    1783             :     } else {
    1784           0 :       nested_messages->push_back(nested_desc);
    1785             :     }
    1786             :   }
    1787           0 : }
    1788             : 
    1789             : // Utility function for PrintFreeFieldNumbers.
    1790             : // Actually prints the formatted free field numbers for given message name and
    1791             : // occupied ranges.
    1792           0 : void FormatFreeFieldNumbers(const string& name,
    1793             :                             const set<FieldRange>& ranges) {
    1794             :   string output;
    1795           0 :   StringAppendF(&output, "%-35s free:", name.c_str());
    1796           0 :   int next_free_number = 1;
    1797           0 :   for (set<FieldRange>::const_iterator i = ranges.begin();
    1798           0 :        i != ranges.end(); ++i) {
    1799             :     // This happens when groups re-use parent field numbers, in which
    1800             :     // case we skip the FieldRange entirely.
    1801           0 :     if (next_free_number >= i->second) continue;
    1802             : 
    1803           0 :     if (next_free_number < i->first) {
    1804           0 :       if (next_free_number + 1 == i->first) {
    1805             :         // Singleton
    1806           0 :         StringAppendF(&output, " %d", next_free_number);
    1807             :       } else {
    1808             :         // Range
    1809           0 :         StringAppendF(&output, " %d-%d", next_free_number, i->first - 1);
    1810             :       }
    1811             :     }
    1812           0 :     next_free_number = i->second;
    1813             :   }
    1814           0 :   if (next_free_number <= FieldDescriptor::kMaxNumber) {
    1815           0 :     StringAppendF(&output, " %d-INF", next_free_number);
    1816             :   }
    1817           0 :   std::cout << output << std::endl;
    1818           0 : }
    1819             : 
    1820             : }  // namespace
    1821             : 
    1822           0 : void CommandLineInterface::PrintFreeFieldNumbers(
    1823           0 :     const Descriptor* descriptor) {
    1824             :   set<FieldRange> ranges;
    1825             :   vector<const Descriptor*> nested_messages;
    1826           0 :   GatherOccupiedFieldRanges(descriptor, &ranges, &nested_messages);
    1827             : 
    1828           0 :   for (int i = 0; i < nested_messages.size(); ++i) {
    1829           0 :     PrintFreeFieldNumbers(nested_messages[i]);
    1830             :   }
    1831           0 :   FormatFreeFieldNumbers(descriptor->full_name(), ranges);
    1832           0 : }
    1833             : 
    1834             : 
    1835             : 
    1836             : }  // namespace compiler
    1837             : }  // namespace protobuf
    1838          51 : }  // namespace google

Generated by: LCOV version 1.10