1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include <errno.h>
9 #include <fcntl.h>
10 #ifndef _MSC_VER
11 #include <unistd.h>
12 #endif
13
14 #include <cstring>
15 #include <string>
16
17 #include "absl/strings/ascii.h"
18 #include "absl/strings/str_cat.h"
19 #include "absl/strings/string_view.h"
20 #include "google/protobuf/compiler/objectivec/line_consumer.h"
21 #include "google/protobuf/io/zero_copy_stream.h"
22 #include "google/protobuf/io/zero_copy_stream_impl.h"
23
24 #ifdef _WIN32
25 #include "google/protobuf/io/io_win32.h"
26 #endif
27
28 namespace google {
29 namespace protobuf {
30 namespace compiler {
31 namespace objectivec {
32
33 // <io.h> is transitively included in this file. Import the functions explicitly
34 // in this port namespace to avoid ambiguous definition.
35 namespace posix {
36 #ifdef _WIN32
37 using google::protobuf::io::win32::open;
38 #else // !_WIN32
39 using ::open;
40 #endif // _WIN32
41 } // namespace posix
42
43 namespace {
44
ascii_isnewline(char c)45 bool ascii_isnewline(char c) { return c == '\n' || c == '\r'; }
46
ReadLine(absl::string_view * input,absl::string_view * line)47 bool ReadLine(absl::string_view* input, absl::string_view* line) {
48 for (int len = 0; len < input->size(); ++len) {
49 if (ascii_isnewline((*input)[len])) {
50 *line = absl::string_view(input->data(), len);
51 ++len; // advance over the newline
52 *input = absl::string_view(input->data() + len, input->size() - len);
53 return true;
54 }
55 }
56 return false; // Ran out of input with no newline.
57 }
58
RemoveComment(absl::string_view * input)59 void RemoveComment(absl::string_view* input) {
60 int offset = input->find('#');
61 if (offset != absl::string_view::npos) {
62 input->remove_suffix(input->length() - offset);
63 }
64 }
65
66 class Parser {
67 public:
Parser(LineConsumer * line_consumer)68 explicit Parser(LineConsumer* line_consumer)
69 : line_consumer_(line_consumer), line_(0) {}
70
71 // Feeds in some input, parse what it can, returning success/failure. Calling
72 // again after an error is undefined.
73 bool ParseChunk(absl::string_view chunk, std::string* out_error);
74
75 // Should be called to finish parsing (after all input has been provided via
76 // successful calls to ParseChunk(), calling after a ParseChunk() failure is
77 // undefined). Returns success/failure.
78 bool Finish(std::string* out_error);
79
last_line() const80 int last_line() const { return line_; }
81
82 private:
83 LineConsumer* line_consumer_;
84 int line_;
85 std::string leftover_;
86 };
87
ParseChunk(absl::string_view chunk,std::string * out_error)88 bool Parser::ParseChunk(absl::string_view chunk, std::string* out_error) {
89 absl::string_view full_chunk;
90 if (!leftover_.empty()) {
91 leftover_ += std::string(chunk);
92 full_chunk = absl::string_view(leftover_);
93 } else {
94 full_chunk = chunk;
95 }
96
97 absl::string_view line;
98 while (ReadLine(&full_chunk, &line)) {
99 ++line_;
100 RemoveComment(&line);
101 line = absl::StripAsciiWhitespace(line);
102 if (!line.empty() && !line_consumer_->ConsumeLine(line, out_error)) {
103 if (out_error->empty()) {
104 *out_error = "ConsumeLine failed without setting an error.";
105 }
106 leftover_.clear();
107 return false;
108 }
109 }
110
111 if (full_chunk.empty()) {
112 leftover_.clear();
113 } else {
114 leftover_ = std::string(full_chunk);
115 }
116 return true;
117 }
118
Finish(std::string * out_error)119 bool Parser::Finish(std::string* out_error) {
120 // If there is still something to go, flush it with a newline.
121 if (!leftover_.empty() && !ParseChunk("\n", out_error)) {
122 return false;
123 }
124 // This really should never fail if ParseChunk succeeded, but check to be
125 // sure.
126 if (!leftover_.empty()) {
127 *out_error = "ParseSimple Internal error: finished with pending data.";
128 return false;
129 }
130 return true;
131 }
132
133 } // namespace
134
ParseSimpleFile(absl::string_view path,LineConsumer * line_consumer,std::string * out_error)135 bool ParseSimpleFile(absl::string_view path, LineConsumer* line_consumer,
136 std::string* out_error) {
137 int fd;
138 do {
139 fd = posix::open(std::string(path).c_str(), O_RDONLY);
140 } while (fd < 0 && errno == EINTR);
141 if (fd < 0) {
142 *out_error =
143 absl::StrCat("error: Unable to open \"", path, "\", ", strerror(errno));
144 return false;
145 }
146 io::FileInputStream file_stream(fd);
147 file_stream.SetCloseOnDelete(true);
148
149 return ParseSimpleStream(file_stream, path, line_consumer, out_error);
150 }
151
ParseSimpleStream(io::ZeroCopyInputStream & input_stream,absl::string_view stream_name,LineConsumer * line_consumer,std::string * out_error)152 bool ParseSimpleStream(io::ZeroCopyInputStream& input_stream,
153 absl::string_view stream_name,
154 LineConsumer* line_consumer, std::string* out_error) {
155 std::string local_error;
156 Parser parser(line_consumer);
157 const void* buf;
158 int buf_len;
159 while (input_stream.Next(&buf, &buf_len)) {
160 if (buf_len == 0) {
161 continue;
162 }
163
164 if (!parser.ParseChunk(
165 absl::string_view(static_cast<const char*>(buf), buf_len),
166 &local_error)) {
167 *out_error = absl::StrCat("error: ", stream_name, " Line ",
168 parser.last_line(), ", ", local_error);
169 return false;
170 }
171 }
172 if (!parser.Finish(&local_error)) {
173 *out_error = absl::StrCat("error: ", stream_name, " Line ",
174 parser.last_line(), ", ", local_error);
175 return false;
176 }
177 return true;
178 }
179
180 } // namespace objectivec
181 } // namespace compiler
182 } // namespace protobuf
183 } // namespace google
184