• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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