blob: a62a7b9d668876f4bbda98fe7c94bfa51e9e020b [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <climits>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "google/protobuf/compiler/objectivec/line_consumer.h"
#include "google/protobuf/io/zero_copy_stream_impl.h"
#ifdef _WIN32
#include "google/protobuf/io/io_win32.h"
#endif
// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
// error cases, so it seems to be ok to use as a back door for errors.
namespace google {
namespace protobuf {
namespace compiler {
namespace objectivec {
// <io.h> is transitively included in this file. Import the functions explicitly
// in this port namespace to avoid ambiguous definition.
namespace posix {
#ifdef _WIN32
using google::protobuf::io::win32::open;
#else // !_WIN32
using ::open;
#endif // _WIN32
} // namespace posix
namespace {
bool ascii_isnewline(char c) { return c == '\n' || c == '\r'; }
bool ReadLine(absl::string_view* input, absl::string_view* line) {
for (int len = 0; len < input->size(); ++len) {
if (ascii_isnewline((*input)[len])) {
*line = absl::string_view(input->data(), len);
++len; // advance over the newline
*input = absl::string_view(input->data() + len, input->size() - len);
return true;
}
}
return false; // Ran out of input with no newline.
}
void RemoveComment(absl::string_view* input) {
int offset = input->find('#');
if (offset != absl::string_view::npos) {
input->remove_suffix(input->length() - offset);
}
}
class Parser {
public:
explicit Parser(LineConsumer* line_consumer)
: line_consumer_(line_consumer), line_(0) {}
// Feeds in some input, parse what it can, returning success/failure. Calling
// again after an error is undefined.
bool ParseChunk(absl::string_view chunk, std::string* out_error);
// Should be called to finish parsing (after all input has been provided via
// successful calls to ParseChunk(), calling after a ParseChunk() failure is
// undefined). Returns success/failure.
bool Finish(std::string* out_error);
int last_line() const { return line_; }
private:
LineConsumer* line_consumer_;
int line_;
std::string leftover_;
};
bool Parser::ParseChunk(absl::string_view chunk, std::string* out_error) {
absl::string_view full_chunk;
if (!leftover_.empty()) {
leftover_ += std::string(chunk);
full_chunk = absl::string_view(leftover_);
} else {
full_chunk = chunk;
}
absl::string_view line;
while (ReadLine(&full_chunk, &line)) {
++line_;
RemoveComment(&line);
line = absl::StripAsciiWhitespace(line);
if (!line.empty() && !line_consumer_->ConsumeLine(line, out_error)) {
if (out_error->empty()) {
*out_error = "ConsumeLine failed without setting an error.";
}
leftover_.clear();
return false;
}
}
if (full_chunk.empty()) {
leftover_.clear();
} else {
leftover_ = std::string(full_chunk);
}
return true;
}
bool Parser::Finish(std::string* out_error) {
// If there is still something to go, flush it with a newline.
if (!leftover_.empty() && !ParseChunk("\n", out_error)) {
return false;
}
// This really should never fail if ParseChunk succeeded, but check to be
// sure.
if (!leftover_.empty()) {
*out_error = "ParseSimple Internal error: finished with pending data.";
return false;
}
return true;
}
} // namespace
bool ParseSimpleFile(const std::string& path, LineConsumer* line_consumer,
std::string* out_error) {
int fd;
do {
fd = posix::open(path.c_str(), O_RDONLY);
} while (fd < 0 && errno == EINTR);
if (fd < 0) {
*out_error =
absl::StrCat("error: Unable to open \"", path, "\", ", strerror(errno));
return false;
}
io::FileInputStream file_stream(fd);
file_stream.SetCloseOnDelete(true);
return ParseSimpleStream(file_stream, path, line_consumer, out_error);
}
bool ParseSimpleStream(io::ZeroCopyInputStream& input_stream,
const std::string& stream_name,
LineConsumer* line_consumer, std::string* out_error) {
std::string local_error;
Parser parser(line_consumer);
const void* buf;
int buf_len;
while (input_stream.Next(&buf, &buf_len)) {
if (buf_len == 0) {
continue;
}
if (!parser.ParseChunk(
absl::string_view(static_cast<const char*>(buf), buf_len),
&local_error)) {
*out_error = absl::StrCat("error: ", stream_name, " Line ",
parser.last_line(), ", ", local_error);
return false;
}
}
if (!parser.Finish(&local_error)) {
*out_error = absl::StrCat("error: ", stream_name, " Line ",
parser.last_line(), ", ", local_error);
return false;
}
return true;
}
} // namespace objectivec
} // namespace compiler
} // namespace protobuf
} // namespace google