PageRenderTime 71ms CodeModel.GetById 25ms RepoModel.GetById 2ms app.codeStats 0ms

/thirdparty/breakpad/third_party/protobuf/protobuf/src/google/protobuf/compiler/importer.cc

http://github.com/tomahawk-player/tomahawk
C++ | 459 lines | 289 code | 56 blank | 114 comment | 76 complexity | 503abd9466a1b0580decf5f38d506769 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, GPL-3.0, GPL-2.0
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // http://code.google.com/p/protobuf/
  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. // Author: kenton@google.com (Kenton Varda)
  31. // Based on original Protocol Buffers design by
  32. // Sanjay Ghemawat, Jeff Dean, and others.
  33. #ifdef _MSC_VER
  34. #include <io.h>
  35. #else
  36. #include <unistd.h>
  37. #endif
  38. #include <sys/types.h>
  39. #include <sys/stat.h>
  40. #include <fcntl.h>
  41. #include <errno.h>
  42. #include <algorithm>
  43. #include <google/protobuf/compiler/importer.h>
  44. #include <google/protobuf/compiler/parser.h>
  45. #include <google/protobuf/io/tokenizer.h>
  46. #include <google/protobuf/io/zero_copy_stream_impl.h>
  47. #include <google/protobuf/stubs/strutil.h>
  48. namespace google {
  49. namespace protobuf {
  50. namespace compiler {
  51. #ifdef _WIN32
  52. #ifndef F_OK
  53. #define F_OK 00 // not defined by MSVC for whatever reason
  54. #endif
  55. #include <ctype.h>
  56. #endif
  57. // Returns true if the text looks like a Windows-style absolute path, starting
  58. // with a drive letter. Example: "C:\foo". TODO(kenton): Share this with
  59. // copy in command_line_interface.cc?
  60. static bool IsWindowsAbsolutePath(const string& text) {
  61. #if defined(_WIN32) || defined(__CYGWIN__)
  62. return text.size() >= 3 && text[1] == ':' &&
  63. isalpha(text[0]) &&
  64. (text[2] == '/' || text[2] == '\\') &&
  65. text.find_last_of(':') == 1;
  66. #else
  67. return false;
  68. #endif
  69. }
  70. MultiFileErrorCollector::~MultiFileErrorCollector() {}
  71. // This class serves two purposes:
  72. // - It implements the ErrorCollector interface (used by Tokenizer and Parser)
  73. // in terms of MultiFileErrorCollector, using a particular filename.
  74. // - It lets us check if any errors have occurred.
  75. class SourceTreeDescriptorDatabase::SingleFileErrorCollector
  76. : public io::ErrorCollector {
  77. public:
  78. SingleFileErrorCollector(const string& filename,
  79. MultiFileErrorCollector* multi_file_error_collector)
  80. : filename_(filename),
  81. multi_file_error_collector_(multi_file_error_collector),
  82. had_errors_(false) {}
  83. ~SingleFileErrorCollector() {}
  84. bool had_errors() { return had_errors_; }
  85. // implements ErrorCollector ---------------------------------------
  86. void AddError(int line, int column, const string& message) {
  87. if (multi_file_error_collector_ != NULL) {
  88. multi_file_error_collector_->AddError(filename_, line, column, message);
  89. }
  90. had_errors_ = true;
  91. }
  92. private:
  93. string filename_;
  94. MultiFileErrorCollector* multi_file_error_collector_;
  95. bool had_errors_;
  96. };
  97. // ===================================================================
  98. SourceTreeDescriptorDatabase::SourceTreeDescriptorDatabase(
  99. SourceTree* source_tree)
  100. : source_tree_(source_tree),
  101. error_collector_(NULL),
  102. using_validation_error_collector_(false),
  103. validation_error_collector_(this) {}
  104. SourceTreeDescriptorDatabase::~SourceTreeDescriptorDatabase() {}
  105. bool SourceTreeDescriptorDatabase::FindFileByName(
  106. const string& filename, FileDescriptorProto* output) {
  107. scoped_ptr<io::ZeroCopyInputStream> input(source_tree_->Open(filename));
  108. if (input == NULL) {
  109. if (error_collector_ != NULL) {
  110. error_collector_->AddError(filename, -1, 0, "File not found.");
  111. }
  112. return false;
  113. }
  114. // Set up the tokenizer and parser.
  115. SingleFileErrorCollector file_error_collector(filename, error_collector_);
  116. io::Tokenizer tokenizer(input.get(), &file_error_collector);
  117. Parser parser;
  118. if (error_collector_ != NULL) {
  119. parser.RecordErrorsTo(&file_error_collector);
  120. }
  121. if (using_validation_error_collector_) {
  122. parser.RecordSourceLocationsTo(&source_locations_);
  123. }
  124. // Parse it.
  125. output->set_name(filename);
  126. return parser.Parse(&tokenizer, output) &&
  127. !file_error_collector.had_errors();
  128. }
  129. bool SourceTreeDescriptorDatabase::FindFileContainingSymbol(
  130. const string& symbol_name, FileDescriptorProto* output) {
  131. return false;
  132. }
  133. bool SourceTreeDescriptorDatabase::FindFileContainingExtension(
  134. const string& containing_type, int field_number,
  135. FileDescriptorProto* output) {
  136. return false;
  137. }
  138. // -------------------------------------------------------------------
  139. SourceTreeDescriptorDatabase::ValidationErrorCollector::
  140. ValidationErrorCollector(SourceTreeDescriptorDatabase* owner)
  141. : owner_(owner) {}
  142. SourceTreeDescriptorDatabase::ValidationErrorCollector::
  143. ~ValidationErrorCollector() {}
  144. void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError(
  145. const string& filename,
  146. const string& element_name,
  147. const Message* descriptor,
  148. ErrorLocation location,
  149. const string& message) {
  150. if (owner_->error_collector_ == NULL) return;
  151. int line, column;
  152. owner_->source_locations_.Find(descriptor, location, &line, &column);
  153. owner_->error_collector_->AddError(filename, line, column, message);
  154. }
  155. // ===================================================================
  156. Importer::Importer(SourceTree* source_tree,
  157. MultiFileErrorCollector* error_collector)
  158. : database_(source_tree),
  159. pool_(&database_, database_.GetValidationErrorCollector()) {
  160. database_.RecordErrorsTo(error_collector);
  161. }
  162. Importer::~Importer() {}
  163. const FileDescriptor* Importer::Import(const string& filename) {
  164. return pool_.FindFileByName(filename);
  165. }
  166. // ===================================================================
  167. SourceTree::~SourceTree() {}
  168. DiskSourceTree::DiskSourceTree() {}
  169. DiskSourceTree::~DiskSourceTree() {}
  170. static inline char LastChar(const string& str) {
  171. return str[str.size() - 1];
  172. }
  173. // Given a path, returns an equivalent path with these changes:
  174. // - On Windows, any backslashes are replaced with forward slashes.
  175. // - Any instances of the directory "." are removed.
  176. // - Any consecutive '/'s are collapsed into a single slash.
  177. // Note that the resulting string may be empty.
  178. //
  179. // TODO(kenton): It would be nice to handle "..", e.g. so that we can figure
  180. // out that "foo/bar.proto" is inside "baz/../foo". However, if baz is a
  181. // symlink or doesn't exist, then things get complicated, and we can't
  182. // actually determine this without investigating the filesystem, probably
  183. // in non-portable ways. So, we punt.
  184. //
  185. // TODO(kenton): It would be nice to use realpath() here except that it
  186. // resolves symbolic links. This could cause problems if people place
  187. // symbolic links in their source tree. For example, if you executed:
  188. // protoc --proto_path=foo foo/bar/baz.proto
  189. // then if foo/bar is a symbolic link, foo/bar/baz.proto will canonicalize
  190. // to a path which does not appear to be under foo, and thus the compiler
  191. // will complain that baz.proto is not inside the --proto_path.
  192. static string CanonicalizePath(string path) {
  193. #ifdef _WIN32
  194. // The Win32 API accepts forward slashes as a path delimiter even though
  195. // backslashes are standard. Let's avoid confusion and use only forward
  196. // slashes.
  197. if (HasPrefixString(path, "\\\\")) {
  198. // Avoid converting two leading backslashes.
  199. path = "\\\\" + StringReplace(path.substr(2), "\\", "/", true);
  200. } else {
  201. path = StringReplace(path, "\\", "/", true);
  202. }
  203. #endif
  204. vector<string> parts;
  205. vector<string> canonical_parts;
  206. SplitStringUsing(path, "/", &parts); // Note: Removes empty parts.
  207. for (int i = 0; i < parts.size(); i++) {
  208. if (parts[i] == ".") {
  209. // Ignore.
  210. } else {
  211. canonical_parts.push_back(parts[i]);
  212. }
  213. }
  214. string result = JoinStrings(canonical_parts, "/");
  215. if (!path.empty() && path[0] == '/') {
  216. // Restore leading slash.
  217. result = '/' + result;
  218. }
  219. if (!path.empty() && LastChar(path) == '/' &&
  220. !result.empty() && LastChar(result) != '/') {
  221. // Restore trailing slash.
  222. result += '/';
  223. }
  224. return result;
  225. }
  226. static inline bool ContainsParentReference(const string& path) {
  227. return path == ".." ||
  228. HasPrefixString(path, "../") ||
  229. HasSuffixString(path, "/..") ||
  230. path.find("/../") != string::npos;
  231. }
  232. // Maps a file from an old location to a new one. Typically, old_prefix is
  233. // a virtual path and new_prefix is its corresponding disk path. Returns
  234. // false if the filename did not start with old_prefix, otherwise replaces
  235. // old_prefix with new_prefix and stores the result in *result. Examples:
  236. // string result;
  237. // assert(ApplyMapping("foo/bar", "", "baz", &result));
  238. // assert(result == "baz/foo/bar");
  239. //
  240. // assert(ApplyMapping("foo/bar", "foo", "baz", &result));
  241. // assert(result == "baz/bar");
  242. //
  243. // assert(ApplyMapping("foo", "foo", "bar", &result));
  244. // assert(result == "bar");
  245. //
  246. // assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
  247. // assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
  248. // assert(!ApplyMapping("foobar", "foo", "baz", &result));
  249. static bool ApplyMapping(const string& filename,
  250. const string& old_prefix,
  251. const string& new_prefix,
  252. string* result) {
  253. if (old_prefix.empty()) {
  254. // old_prefix matches any relative path.
  255. if (ContainsParentReference(filename)) {
  256. // We do not allow the file name to use "..".
  257. return false;
  258. }
  259. if (HasPrefixString(filename, "/") ||
  260. IsWindowsAbsolutePath(filename)) {
  261. // This is an absolute path, so it isn't matched by the empty string.
  262. return false;
  263. }
  264. result->assign(new_prefix);
  265. if (!result->empty()) result->push_back('/');
  266. result->append(filename);
  267. return true;
  268. } else if (HasPrefixString(filename, old_prefix)) {
  269. // old_prefix is a prefix of the filename. Is it the whole filename?
  270. if (filename.size() == old_prefix.size()) {
  271. // Yep, it's an exact match.
  272. *result = new_prefix;
  273. return true;
  274. } else {
  275. // Not an exact match. Is the next character a '/'? Otherwise,
  276. // this isn't actually a match at all. E.g. the prefix "foo/bar"
  277. // does not match the filename "foo/barbaz".
  278. int after_prefix_start = -1;
  279. if (filename[old_prefix.size()] == '/') {
  280. after_prefix_start = old_prefix.size() + 1;
  281. } else if (filename[old_prefix.size() - 1] == '/') {
  282. // old_prefix is never empty, and canonicalized paths never have
  283. // consecutive '/' characters.
  284. after_prefix_start = old_prefix.size();
  285. }
  286. if (after_prefix_start != -1) {
  287. // Yep. So the prefixes are directories and the filename is a file
  288. // inside them.
  289. string after_prefix = filename.substr(after_prefix_start);
  290. if (ContainsParentReference(after_prefix)) {
  291. // We do not allow the file name to use "..".
  292. return false;
  293. }
  294. result->assign(new_prefix);
  295. if (!result->empty()) result->push_back('/');
  296. result->append(after_prefix);
  297. return true;
  298. }
  299. }
  300. }
  301. return false;
  302. }
  303. void DiskSourceTree::MapPath(const string& virtual_path,
  304. const string& disk_path) {
  305. mappings_.push_back(Mapping(virtual_path, CanonicalizePath(disk_path)));
  306. }
  307. DiskSourceTree::DiskFileToVirtualFileResult
  308. DiskSourceTree::DiskFileToVirtualFile(
  309. const string& disk_file,
  310. string* virtual_file,
  311. string* shadowing_disk_file) {
  312. int mapping_index = -1;
  313. string canonical_disk_file = CanonicalizePath(disk_file);
  314. for (int i = 0; i < mappings_.size(); i++) {
  315. // Apply the mapping in reverse.
  316. if (ApplyMapping(canonical_disk_file, mappings_[i].disk_path,
  317. mappings_[i].virtual_path, virtual_file)) {
  318. // Success.
  319. mapping_index = i;
  320. break;
  321. }
  322. }
  323. if (mapping_index == -1) {
  324. return NO_MAPPING;
  325. }
  326. // Iterate through all mappings with higher precedence and verify that none
  327. // of them map this file to some other existing file.
  328. for (int i = 0; i < mapping_index; i++) {
  329. if (ApplyMapping(*virtual_file, mappings_[i].virtual_path,
  330. mappings_[i].disk_path, shadowing_disk_file)) {
  331. if (access(shadowing_disk_file->c_str(), F_OK) >= 0) {
  332. // File exists.
  333. return SHADOWED;
  334. }
  335. }
  336. }
  337. shadowing_disk_file->clear();
  338. // Verify that we can open the file. Note that this also has the side-effect
  339. // of verifying that we are not canonicalizing away any non-existent
  340. // directories.
  341. scoped_ptr<io::ZeroCopyInputStream> stream(OpenDiskFile(disk_file));
  342. if (stream == NULL) {
  343. return CANNOT_OPEN;
  344. }
  345. return SUCCESS;
  346. }
  347. bool DiskSourceTree::VirtualFileToDiskFile(const string& virtual_file,
  348. string* disk_file) {
  349. scoped_ptr<io::ZeroCopyInputStream> stream(OpenVirtualFile(virtual_file,
  350. disk_file));
  351. return stream != NULL;
  352. }
  353. io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) {
  354. return OpenVirtualFile(filename, NULL);
  355. }
  356. io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
  357. const string& virtual_file,
  358. string* disk_file) {
  359. if (virtual_file != CanonicalizePath(virtual_file) ||
  360. ContainsParentReference(virtual_file)) {
  361. // We do not allow importing of paths containing things like ".." or
  362. // consecutive slashes since the compiler expects files to be uniquely
  363. // identified by file name.
  364. return NULL;
  365. }
  366. for (int i = 0; i < mappings_.size(); i++) {
  367. string temp_disk_file;
  368. if (ApplyMapping(virtual_file, mappings_[i].virtual_path,
  369. mappings_[i].disk_path, &temp_disk_file)) {
  370. io::ZeroCopyInputStream* stream = OpenDiskFile(temp_disk_file);
  371. if (stream != NULL) {
  372. if (disk_file != NULL) {
  373. *disk_file = temp_disk_file;
  374. }
  375. return stream;
  376. }
  377. if (errno == EACCES) {
  378. // The file exists but is not readable.
  379. // TODO(kenton): Find a way to report this more nicely.
  380. GOOGLE_LOG(WARNING) << "Read access is denied for file: " << temp_disk_file;
  381. return NULL;
  382. }
  383. }
  384. }
  385. return NULL;
  386. }
  387. io::ZeroCopyInputStream* DiskSourceTree::OpenDiskFile(
  388. const string& filename) {
  389. int file_descriptor;
  390. do {
  391. file_descriptor = open(filename.c_str(), O_RDONLY);
  392. } while (file_descriptor < 0 && errno == EINTR);
  393. if (file_descriptor >= 0) {
  394. io::FileInputStream* result = new io::FileInputStream(file_descriptor);
  395. result->SetCloseOnDelete(true);
  396. return result;
  397. } else {
  398. return NULL;
  399. }
  400. }
  401. } // namespace compiler
  402. } // namespace protobuf
  403. } // namespace google