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