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#ifndef _MSC_VER
32#include <unistd.h>
33#endif
34#include <climits>
35#include <errno.h>
36#include <fcntl.h>
37#include <fstream>
38#include <iostream>
39#include <sstream>
40#include <stdlib.h>
41#include <unordered_set>
42#include <vector>
43
44#include <google/protobuf/compiler/code_generator.h>
45#include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
46#include <google/protobuf/compiler/objectivec/objectivec_nsobject_methods.h>
47#include <google/protobuf/descriptor.pb.h>
48#include <google/protobuf/io/coded_stream.h>
49#include <google/protobuf/io/printer.h>
50#include <google/protobuf/io/zero_copy_stream_impl.h>
51#include <google/protobuf/io/io_win32.h>
52#include <google/protobuf/port.h>
53#include <google/protobuf/stubs/common.h>
54#include <google/protobuf/stubs/strutil.h>
55
56// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
57// error cases, so it seems to be ok to use as a back door for errors.
58
59namespace google {
60namespace protobuf {
61namespace compiler {
62namespace objectivec {
63
64// <io.h> is transitively included in this file. Import the functions explicitly
65// in this port namespace to avoid ambiguous definition.
66namespace posix {
67#ifdef _WIN32
68using ::google::protobuf::io::win32::open;
69#else
70using ::open;
71#endif
72} // namespace port
73
74namespace {
75
76bool BoolFromEnvVar(const char* env_var, bool default_value) {
77 const char* value = getenv(name: env_var);
78 if (value) {
79 return std::string("YES") == ToUpper(s: value);
80 }
81 return default_value;
82}
83
84class SimpleLineCollector : public LineConsumer {
85 public:
86 SimpleLineCollector(std::unordered_set<std::string>* inout_set)
87 : set_(inout_set) {}
88
89 virtual bool ConsumeLine(const StringPiece& line, std::string* out_error) override {
90 set_->insert(x: std::string(line));
91 return true;
92 }
93
94 private:
95 std::unordered_set<std::string>* set_;
96};
97
98class PackageToPrefixesCollector : public LineConsumer {
99 public:
100 PackageToPrefixesCollector(const std::string &usage,
101 std::map<std::string, std::string>* inout_package_to_prefix_map)
102 : usage_(usage), prefix_map_(inout_package_to_prefix_map) {}
103
104 virtual bool ConsumeLine(const StringPiece& line, std::string* out_error) override;
105
106 private:
107 const std::string usage_;
108 std::map<std::string, std::string>* prefix_map_;
109};
110
111class PrefixModeStorage {
112 public:
113 PrefixModeStorage();
114
115 const std::string package_to_prefix_mappings_path() const { return package_to_prefix_mappings_path_; }
116 void set_package_to_prefix_mappings_path(const std::string& path) {
117 package_to_prefix_mappings_path_ = path;
118 package_to_prefix_map_.clear();
119 }
120
121 std::string prefix_from_proto_package_mappings(const FileDescriptor* file);
122
123 bool use_package_name() const { return use_package_name_; }
124 void set_use_package_name(bool on_or_off) { use_package_name_ = on_or_off; }
125
126 const std::string exception_path() const { return exception_path_; }
127 void set_exception_path(const std::string& path) {
128 exception_path_ = path;
129 exceptions_.clear();
130 }
131
132 bool is_package_exempted(const std::string& package);
133
134 // When using a proto package as the prefix, this should be added as the
135 // prefix in front of it.
136 const std::string& forced_package_prefix() const { return forced_prefix_; }
137
138 private:
139 bool use_package_name_;
140 std::map<std::string, std::string> package_to_prefix_map_;
141 std::string package_to_prefix_mappings_path_;
142 std::string exception_path_;
143 std::string forced_prefix_;
144 std::unordered_set<std::string> exceptions_;
145};
146
147PrefixModeStorage::PrefixModeStorage() {
148 // Even thought there are generation options, have an env back door since some
149 // of these helpers could be used in other plugins.
150
151 use_package_name_ = BoolFromEnvVar(env_var: "GPB_OBJC_USE_PACKAGE_AS_PREFIX", default_value: false);
152
153 const char* exception_path = getenv(name: "GPB_OBJC_PACKAGE_PREFIX_EXCEPTIONS_PATH");
154 if (exception_path) {
155 exception_path_ = exception_path;
156 }
157
158 // This one is a not expected to be common, so it doesn't get a generation
159 // option, just the env var.
160 const char* prefix = getenv(name: "GPB_OBJC_USE_PACKAGE_AS_PREFIX_PREFIX");
161 if (prefix) {
162 forced_prefix_ = prefix;
163 }
164}
165
166std::string PrefixModeStorage::prefix_from_proto_package_mappings(const FileDescriptor* file) {
167 if (!file) {
168 return "";
169 }
170
171 if (package_to_prefix_map_.empty() && !package_to_prefix_mappings_path_.empty()) {
172 std::string error_str;
173 // Re use the same collector as we use for expected_prefixes_path since the file
174 // format is the same.
175 PackageToPrefixesCollector collector("Package to prefixes", &package_to_prefix_map_);
176 if (!ParseSimpleFile(path: package_to_prefix_mappings_path_, line_consumer: &collector, out_error: &error_str)) {
177 if (error_str.empty()) {
178 error_str = std::string("protoc:0: warning: Failed to parse")
179 + std::string(" prefix to proto package mappings file: ")
180 + package_to_prefix_mappings_path_;
181 }
182 std::cerr << error_str << std::endl;
183 std::cerr.flush();
184 package_to_prefix_map_.clear();
185 }
186 }
187
188 const std::string package = file->package();
189 // For files without packages, the can be registered as "no_package:PATH",
190 // allowing the expected prefixes file.
191 static const std::string no_package_prefix("no_package:");
192 const std::string lookup_key = package.empty() ? no_package_prefix + file->name() : package;
193
194 std::map<std::string, std::string>::const_iterator prefix_lookup =
195 package_to_prefix_map_.find(x: lookup_key);
196
197 if (prefix_lookup != package_to_prefix_map_.end()) {
198 return prefix_lookup->second;
199 }
200
201 return "";
202}
203
204bool PrefixModeStorage::is_package_exempted(const std::string& package) {
205 if (exceptions_.empty() && !exception_path_.empty()) {
206 std::string error_str;
207 SimpleLineCollector collector(&exceptions_);
208 if (!ParseSimpleFile(path: exception_path_, line_consumer: &collector, out_error: &error_str)) {
209 if (error_str.empty()) {
210 error_str = std::string("protoc:0: warning: Failed to parse")
211 + std::string(" package prefix exceptions file: ")
212 + exception_path_;
213 }
214 std::cerr << error_str << std::endl;
215 std::cerr.flush();
216 exceptions_.clear();
217 }
218
219 // If the file was empty put something in it so it doesn't get reloaded over
220 // and over.
221 if (exceptions_.empty()) {
222 exceptions_.insert(x: "<not a real package>");
223 }
224 }
225
226 return exceptions_.count(x: package) != 0;
227}
228
229PrefixModeStorage g_prefix_mode;
230
231} // namespace
232
233std::string GetPackageToPrefixMappingsPath() {
234 return g_prefix_mode.package_to_prefix_mappings_path();
235}
236
237void SetPackageToPrefixMappingsPath(const std::string& file_path) {
238 g_prefix_mode.set_package_to_prefix_mappings_path(file_path);
239}
240
241bool UseProtoPackageAsDefaultPrefix() {
242 return g_prefix_mode.use_package_name();
243}
244
245void SetUseProtoPackageAsDefaultPrefix(bool on_or_off) {
246 g_prefix_mode.set_use_package_name(on_or_off);
247}
248
249std::string GetProtoPackagePrefixExceptionList() {
250 return g_prefix_mode.exception_path();
251}
252
253void SetProtoPackagePrefixExceptionList(const std::string& file_path) {
254 g_prefix_mode.set_exception_path(file_path);
255}
256
257Options::Options() {
258 // While there are generator options, also support env variables to help with
259 // build systems where it isn't as easy to hook in for add the generation
260 // options when invoking protoc.
261 const char* file_path = getenv(name: "GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
262 if (file_path) {
263 expected_prefixes_path = file_path;
264 }
265 const char* suppressions = getenv(name: "GPB_OBJC_EXPECTED_PACKAGE_PREFIXES_SUPPRESSIONS");
266 if (suppressions) {
267 expected_prefixes_suppressions =
268 Split(full: suppressions, delim: ";", skip_empty: true);
269 }
270 prefixes_must_be_registered =
271 BoolFromEnvVar(env_var: "GPB_OBJC_PREFIXES_MUST_BE_REGISTERED", default_value: false);
272 require_prefixes = BoolFromEnvVar(env_var: "GPB_OBJC_REQUIRE_PREFIXES", default_value: false);
273}
274
275namespace {
276
277std::unordered_set<std::string> MakeWordsMap(const char* const words[],
278 size_t num_words) {
279 std::unordered_set<std::string> result;
280 for (int i = 0; i < num_words; i++) {
281 result.insert(x: words[i]);
282 }
283 return result;
284}
285
286const char* const kUpperSegmentsList[] = {"url", "http", "https"};
287
288std::unordered_set<std::string> kUpperSegments =
289 MakeWordsMap(words: kUpperSegmentsList, GOOGLE_ARRAYSIZE(kUpperSegmentsList));
290
291bool ascii_isnewline(char c) {
292 return c == '\n' || c == '\r';
293}
294
295// Internal helper for name handing.
296// Do not expose this outside of helpers, stick to having functions for specific
297// cases (ClassName(), FieldName()), so there is always consistent suffix rules.
298std::string UnderscoresToCamelCase(const std::string& input,
299 bool first_capitalized) {
300 std::vector<std::string> values;
301 std::string current;
302
303 bool last_char_was_number = false;
304 bool last_char_was_lower = false;
305 bool last_char_was_upper = false;
306 for (int i = 0; i < input.size(); i++) {
307 char c = input[i];
308 if (ascii_isdigit(c)) {
309 if (!last_char_was_number) {
310 values.push_back(x: current);
311 current = "";
312 }
313 current += c;
314 last_char_was_number = last_char_was_lower = last_char_was_upper = false;
315 last_char_was_number = true;
316 } else if (ascii_islower(c)) {
317 // lowercase letter can follow a lowercase or uppercase letter
318 if (!last_char_was_lower && !last_char_was_upper) {
319 values.push_back(x: current);
320 current = "";
321 }
322 current += c; // already lower
323 last_char_was_number = last_char_was_lower = last_char_was_upper = false;
324 last_char_was_lower = true;
325 } else if (ascii_isupper(c)) {
326 if (!last_char_was_upper) {
327 values.push_back(x: current);
328 current = "";
329 }
330 current += ascii_tolower(c);
331 last_char_was_number = last_char_was_lower = last_char_was_upper = false;
332 last_char_was_upper = true;
333 } else {
334 last_char_was_number = last_char_was_lower = last_char_was_upper = false;
335 }
336 }
337 values.push_back(x: current);
338
339 std::string result;
340 bool first_segment_forces_upper = false;
341 for (std::vector<std::string>::iterator i = values.begin(); i != values.end();
342 ++i) {
343 std::string value = *i;
344 bool all_upper = (kUpperSegments.count(x: value) > 0);
345 if (all_upper && (result.length() == 0)) {
346 first_segment_forces_upper = true;
347 }
348 for (int j = 0; j < value.length(); j++) {
349 if (j == 0 || all_upper) {
350 value[j] = ascii_toupper(c: value[j]);
351 } else {
352 // Nothing, already in lower.
353 }
354 }
355 result += value;
356 }
357 if ((result.length() != 0) &&
358 !first_capitalized &&
359 !first_segment_forces_upper) {
360 result[0] = ascii_tolower(c: result[0]);
361 }
362 return result;
363}
364
365const char* const kReservedWordList[] = {
366 // Note NSObject Methods:
367 // These are brought in from objectivec_nsobject_methods.h that is generated
368 // using method_dump.sh. See kNSObjectMethods below.
369
370 // Objective C "keywords" that aren't in C
371 // From
372 // http://stackoverflow.com/questions/1873630/reserved-keywords-in-objective-c
373 // with some others added on.
374 "id", "_cmd", "super", "in", "out", "inout", "bycopy", "byref", "oneway",
375 "self", "instancetype", "nullable", "nonnull", "nil", "Nil",
376 "YES", "NO", "weak",
377
378 // C/C++ keywords (Incl C++ 0x11)
379 // From http://en.cppreference.com/w/cpp/keywords
380 "and", "and_eq", "alignas", "alignof", "asm", "auto", "bitand", "bitor",
381 "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class",
382 "compl", "const", "constexpr", "const_cast", "continue", "decltype",
383 "default", "delete", "double", "dynamic_cast", "else", "enum", "explicit",
384 "export", "extern ", "false", "float", "for", "friend", "goto", "if",
385 "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not",
386 "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected",
387 "public", "register", "reinterpret_cast", "return", "short", "signed",
388 "sizeof", "static", "static_assert", "static_cast", "struct", "switch",
389 "template", "this", "thread_local", "throw", "true", "try", "typedef",
390 "typeid", "typename", "union", "unsigned", "using", "virtual", "void",
391 "volatile", "wchar_t", "while", "xor", "xor_eq",
392
393 // C99 keywords
394 // From
395 // http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fkeyw.htm
396 "restrict",
397
398 // GCC/Clang extension
399 "typeof",
400
401 // Not a keyword, but will break you
402 "NULL",
403
404 // C88+ specs call for these to be macros, so depending on what they are
405 // defined to be it can lead to odd errors for some Xcode/SDK versions.
406 "stdin", "stdout", "stderr",
407
408 // Objective-C Runtime typedefs
409 // From <obc/runtime.h>
410 "Category", "Ivar", "Method", "Protocol",
411
412 // GPBMessage Methods
413 // Only need to add instance methods that may conflict with
414 // method declared in protos. The main cases are methods
415 // that take no arguments, or setFoo:/hasFoo: type methods.
416 "clear", "data", "delimitedData", "descriptor", "extensionRegistry",
417 "extensionsCurrentlySet", "initialized", "isInitialized", "serializedSize",
418 "sortedExtensionsInUse", "unknownFields",
419
420 // MacTypes.h names
421 "Fixed", "Fract", "Size", "LogicalAddress", "PhysicalAddress", "ByteCount",
422 "ByteOffset", "Duration", "AbsoluteTime", "OptionBits", "ItemCount",
423 "PBVersion", "ScriptCode", "LangCode", "RegionCode", "OSType",
424 "ProcessSerialNumber", "Point", "Rect", "FixedPoint", "FixedRect", "Style",
425 "StyleParameter", "StyleField", "TimeScale", "TimeBase", "TimeRecord",
426};
427
428// returns true is input starts with __ or _[A-Z] which are reserved identifiers
429// in C/ C++. All calls should go through UnderscoresToCamelCase before getting here
430// but this verifies and allows for future expansion if we decide to redefine what a
431// reserved C identifier is (for example the GNU list
432// https://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html )
433bool IsReservedCIdentifier(const std::string& input) {
434 if (input.length() > 2) {
435 if (input.at(n: 0) == '_') {
436 if (isupper(input.at(n: 1)) || input.at(n: 1) == '_') {
437 return true;
438 }
439 }
440 }
441 return false;
442}
443
444std::string SanitizeNameForObjC(const std::string& prefix,
445 const std::string& input,
446 const std::string& extension,
447 std::string* out_suffix_added) {
448 static const std::unordered_set<std::string> kReservedWords =
449 MakeWordsMap(words: kReservedWordList, GOOGLE_ARRAYSIZE(kReservedWordList));
450 static const std::unordered_set<std::string> kNSObjectMethods =
451 MakeWordsMap(words: kNSObjectMethodsList, GOOGLE_ARRAYSIZE(kNSObjectMethodsList));
452 std::string sanitized;
453 // We add the prefix in the cases where the string is missing a prefix.
454 // We define "missing a prefix" as where 'input':
455 // a) Doesn't start with the prefix or
456 // b) Isn't equivalent to the prefix or
457 // c) Has the prefix, but the letter after the prefix is lowercase
458 if (HasPrefixString(str: input, prefix)) {
459 if (input.length() == prefix.length() || !ascii_isupper(c: input[prefix.length()])) {
460 sanitized = prefix + input;
461 } else {
462 sanitized = input;
463 }
464 } else {
465 sanitized = prefix + input;
466 }
467 if (IsReservedCIdentifier(input: sanitized) ||
468 (kReservedWords.count(x: sanitized) > 0) ||
469 (kNSObjectMethods.count(x: sanitized) > 0)) {
470 if (out_suffix_added) *out_suffix_added = extension;
471 return sanitized + extension;
472 }
473 if (out_suffix_added) out_suffix_added->clear();
474 return sanitized;
475}
476
477std::string NameFromFieldDescriptor(const FieldDescriptor* field) {
478 if (field->type() == FieldDescriptor::TYPE_GROUP) {
479 return field->message_type()->name();
480 } else {
481 return field->name();
482 }
483}
484
485void PathSplit(const std::string& path, std::string* directory,
486 std::string* basename) {
487 std::string::size_type last_slash = path.rfind(c: '/');
488 if (last_slash == std::string::npos) {
489 if (directory) {
490 *directory = "";
491 }
492 if (basename) {
493 *basename = path;
494 }
495 } else {
496 if (directory) {
497 *directory = path.substr(pos: 0, n: last_slash);
498 }
499 if (basename) {
500 *basename = path.substr(pos: last_slash + 1);
501 }
502 }
503}
504
505bool IsSpecialName(const std::string& name, const std::string* special_names,
506 size_t count) {
507 for (size_t i = 0; i < count; ++i) {
508 size_t length = special_names[i].length();
509 if (name.compare(pos: 0, n: length, str: special_names[i]) == 0) {
510 if (name.length() > length) {
511 // If name is longer than the retained_name[i] that it matches
512 // the next character must be not lower case (newton vs newTon vs
513 // new_ton).
514 return !ascii_islower(c: name[length]);
515 } else {
516 return true;
517 }
518 }
519 }
520 return false;
521}
522
523std::string GetZeroEnumNameForFlagType(const FlagType flag_type) {
524 switch(flag_type) {
525 case FLAGTYPE_DESCRIPTOR_INITIALIZATION:
526 return "GPBDescriptorInitializationFlag_None";
527 case FLAGTYPE_EXTENSION:
528 return "GPBExtensionNone";
529 case FLAGTYPE_FIELD:
530 return "GPBFieldNone";
531 default:
532 GOOGLE_LOG(FATAL) << "Can't get here.";
533 return "0";
534 }
535}
536
537std::string GetEnumNameForFlagType(const FlagType flag_type) {
538 switch(flag_type) {
539 case FLAGTYPE_DESCRIPTOR_INITIALIZATION:
540 return "GPBDescriptorInitializationFlags";
541 case FLAGTYPE_EXTENSION:
542 return "GPBExtensionOptions";
543 case FLAGTYPE_FIELD:
544 return "GPBFieldFlags";
545 default:
546 GOOGLE_LOG(FATAL) << "Can't get here.";
547 return std::string();
548 }
549}
550
551void MaybeUnQuote(StringPiece* input) {
552 if ((input->length() >= 2) &&
553 ((*input->data() == '\'' || *input->data() == '"')) &&
554 ((*input)[input->length() - 1] == *input->data())) {
555 input->remove_prefix(n: 1);
556 input->remove_suffix(n: 1);
557 }
558}
559
560} // namespace
561
562// Escape C++ trigraphs by escaping question marks to \?
563std::string EscapeTrigraphs(const std::string& to_escape) {
564 return StringReplace(s: to_escape, oldsub: "?", newsub: "\\?", replace_all: true);
565}
566
567void TrimWhitespace(StringPiece* input) {
568 while (!input->empty() && ascii_isspace(c: *input->data())) {
569 input->remove_prefix(n: 1);
570 }
571 while (!input->empty() && ascii_isspace(c: (*input)[input->length() - 1])) {
572 input->remove_suffix(n: 1);
573 }
574}
575
576bool IsRetainedName(const std::string& name) {
577 // List of prefixes from
578 // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
579 static const std::string retained_names[] = {"new", "alloc", "copy",
580 "mutableCopy"};
581 return IsSpecialName(name, special_names: retained_names,
582 count: sizeof(retained_names) / sizeof(retained_names[0]));
583}
584
585bool IsInitName(const std::string& name) {
586 static const std::string init_names[] = {"init"};
587 return IsSpecialName(name, special_names: init_names,
588 count: sizeof(init_names) / sizeof(init_names[0]));
589}
590
591std::string BaseFileName(const FileDescriptor* file) {
592 std::string basename;
593 PathSplit(path: file->name(), NULL, basename: &basename);
594 return basename;
595}
596
597std::string FileClassPrefix(const FileDescriptor* file) {
598 // Always honor the file option.
599 if (file->options().has_objc_class_prefix()) {
600 return file->options().objc_class_prefix();
601 }
602
603 // If package prefix is specified in an prefix to proto mappings file then use that.
604 std::string objc_class_prefix = g_prefix_mode.prefix_from_proto_package_mappings(file);
605 if (!objc_class_prefix.empty()) {
606 return objc_class_prefix;
607 }
608
609 // If package prefix isn't enabled, done.
610 if (!g_prefix_mode.use_package_name()) {
611 return "";
612 }
613
614 // If the package is in the exceptions list, done.
615 if (g_prefix_mode.is_package_exempted(package: file->package())) {
616 return "";
617 }
618
619 // Transform the package into a prefix: use the dot segments as part,
620 // camelcase each one and then join them with underscores, and add an
621 // underscore at the end.
622 std::string result;
623 const std::vector<std::string> segments = Split(full: file->package(), delim: ".", skip_empty: true);
624 for (const auto& segment : segments) {
625 const std::string part = UnderscoresToCamelCase(input: segment, first_capitalized: true);
626 if (part.empty()) {
627 continue;
628 }
629 if (!result.empty()) {
630 result.append(s: "_");
631 }
632 result.append(str: part);
633 }
634 if (!result.empty()) {
635 result.append(s: "_");
636 }
637 return g_prefix_mode.forced_package_prefix() + result;
638}
639
640std::string FilePath(const FileDescriptor* file) {
641 std::string output;
642 std::string basename;
643 std::string directory;
644 PathSplit(path: file->name(), directory: &directory, basename: &basename);
645 if (directory.length() > 0) {
646 output = directory + "/";
647 }
648 basename = StripProto(filename: basename);
649
650 // CamelCase to be more ObjC friendly.
651 basename = UnderscoresToCamelCase(input: basename, first_capitalized: true);
652
653 output += basename;
654 return output;
655}
656
657std::string FilePathBasename(const FileDescriptor* file) {
658 std::string output;
659 std::string basename;
660 std::string directory;
661 PathSplit(path: file->name(), directory: &directory, basename: &basename);
662 basename = StripProto(filename: basename);
663
664 // CamelCase to be more ObjC friendly.
665 output = UnderscoresToCamelCase(input: basename, first_capitalized: true);
666
667 return output;
668}
669
670std::string FileClassName(const FileDescriptor* file) {
671 const std::string prefix = FileClassPrefix(file);
672 const std::string name =
673 UnderscoresToCamelCase(input: StripProto(filename: BaseFileName(file)), first_capitalized: true) + "Root";
674 // There aren't really any reserved words that end in "Root", but playing
675 // it safe and checking.
676 return SanitizeNameForObjC(prefix, input: name, extension: "_RootClass", NULL);
677}
678
679std::string ClassNameWorker(const Descriptor* descriptor) {
680 std::string name;
681 if (descriptor->containing_type() != NULL) {
682 name = ClassNameWorker(descriptor: descriptor->containing_type());
683 name += "_";
684 }
685 return name + descriptor->name();
686}
687
688std::string ClassNameWorker(const EnumDescriptor* descriptor) {
689 std::string name;
690 if (descriptor->containing_type() != NULL) {
691 name = ClassNameWorker(descriptor: descriptor->containing_type());
692 name += "_";
693 }
694 return name + descriptor->name();
695}
696
697std::string ClassName(const Descriptor* descriptor) {
698 return ClassName(descriptor, NULL);
699}
700
701std::string ClassName(const Descriptor* descriptor,
702 std::string* out_suffix_added) {
703 // 1. Message names are used as is (style calls for CamelCase, trust it).
704 // 2. Check for reserved word at the very end and then suffix things.
705 const std::string prefix = FileClassPrefix(file: descriptor->file());
706 const std::string name = ClassNameWorker(descriptor);
707 return SanitizeNameForObjC(prefix, input: name, extension: "_Class", out_suffix_added);
708}
709
710std::string EnumName(const EnumDescriptor* descriptor) {
711 // 1. Enum names are used as is (style calls for CamelCase, trust it).
712 // 2. Check for reserved word at the every end and then suffix things.
713 // message Fixed {
714 // message Size {...}
715 // enum Mumble {...}
716 // ...
717 // }
718 // yields Fixed_Class, Fixed_Size.
719 const std::string prefix = FileClassPrefix(file: descriptor->file());
720 const std::string name = ClassNameWorker(descriptor);
721 return SanitizeNameForObjC(prefix, input: name, extension: "_Enum", NULL);
722}
723
724std::string EnumValueName(const EnumValueDescriptor* descriptor) {
725 // Because of the Switch enum compatibility, the name on the enum has to have
726 // the suffix handing, so it slightly diverges from how nested classes work.
727 // enum Fixed {
728 // FOO = 1
729 // }
730 // yields Fixed_Enum and Fixed_Enum_Foo (not Fixed_Foo).
731 const std::string class_name = EnumName(descriptor: descriptor->type());
732 const std::string value_str =
733 UnderscoresToCamelCase(input: descriptor->name(), first_capitalized: true);
734 const std::string name = class_name + "_" + value_str;
735 // There aren't really any reserved words with an underscore and a leading
736 // capital letter, but playing it safe and checking.
737 return SanitizeNameForObjC(prefix: "", input: name, extension: "_Value", NULL);
738}
739
740std::string EnumValueShortName(const EnumValueDescriptor* descriptor) {
741 // Enum value names (EnumValueName above) are the enum name turned into
742 // a class name and then the value name is CamelCased and concatenated; the
743 // whole thing then gets sanitized for reserved words.
744 // The "short name" is intended to be the final leaf, the value name; but
745 // you can't simply send that off to sanitize as that could result in it
746 // getting modified when the full name didn't. For example enum
747 // "StorageModes" has a value "retain". So the full name is
748 // "StorageModes_Retain", but if we sanitize "retain" it would become
749 // "RetainValue".
750 // So the right way to get the short name is to take the full enum name
751 // and then strip off the enum name (leaving the value name and anything
752 // done by sanitize).
753 const std::string class_name = EnumName(descriptor: descriptor->type());
754 const std::string long_name_prefix = class_name + "_";
755 const std::string long_name = EnumValueName(descriptor);
756 return StripPrefixString(str: long_name, prefix: long_name_prefix);
757}
758
759std::string UnCamelCaseEnumShortName(const std::string& name) {
760 std::string result;
761 for (int i = 0; i < name.size(); i++) {
762 char c = name[i];
763 if (i > 0 && ascii_isupper(c)) {
764 result += '_';
765 }
766 result += ascii_toupper(c);
767 }
768 return result;
769}
770
771std::string ExtensionMethodName(const FieldDescriptor* descriptor) {
772 const std::string name = NameFromFieldDescriptor(field: descriptor);
773 const std::string result = UnderscoresToCamelCase(input: name, first_capitalized: false);
774 return SanitizeNameForObjC(prefix: "", input: result, extension: "_Extension", NULL);
775}
776
777std::string FieldName(const FieldDescriptor* field) {
778 const std::string name = NameFromFieldDescriptor(field);
779 std::string result = UnderscoresToCamelCase(input: name, first_capitalized: false);
780 if (field->is_repeated() && !field->is_map()) {
781 // Add "Array" before do check for reserved worlds.
782 result += "Array";
783 } else {
784 // If it wasn't repeated, but ends in "Array", force on the _p suffix.
785 if (HasSuffixString(str: result, suffix: "Array")) {
786 result += "_p";
787 }
788 }
789 return SanitizeNameForObjC(prefix: "", input: result, extension: "_p", NULL);
790}
791
792std::string FieldNameCapitalized(const FieldDescriptor* field) {
793 // Want the same suffix handling, so upcase the first letter of the other
794 // name.
795 std::string result = FieldName(field);
796 if (result.length() > 0) {
797 result[0] = ascii_toupper(c: result[0]);
798 }
799 return result;
800}
801
802std::string OneofEnumName(const OneofDescriptor* descriptor) {
803 const Descriptor* fieldDescriptor = descriptor->containing_type();
804 std::string name = ClassName(descriptor: fieldDescriptor);
805 name += "_" + UnderscoresToCamelCase(input: descriptor->name(), first_capitalized: true) + "_OneOfCase";
806 // No sanitize needed because the OS never has names that end in _OneOfCase.
807 return name;
808}
809
810std::string OneofName(const OneofDescriptor* descriptor) {
811 std::string name = UnderscoresToCamelCase(input: descriptor->name(), first_capitalized: false);
812 // No sanitize needed because it gets OneOfCase added and that shouldn't
813 // ever conflict.
814 return name;
815}
816
817std::string OneofNameCapitalized(const OneofDescriptor* descriptor) {
818 // Use the common handling and then up-case the first letter.
819 std::string result = OneofName(descriptor);
820 if (result.length() > 0) {
821 result[0] = ascii_toupper(c: result[0]);
822 }
823 return result;
824}
825
826std::string ObjCClass(const std::string& class_name) {
827 return std::string("GPBObjCClass(") + class_name + ")";
828}
829
830std::string ObjCClassDeclaration(const std::string& class_name) {
831 return std::string("GPBObjCClassDeclaration(") + class_name + ");";
832}
833
834std::string UnCamelCaseFieldName(const std::string& name, const FieldDescriptor* field) {
835 std::string worker(name);
836 if (HasSuffixString(str: worker, suffix: "_p")) {
837 worker = StripSuffixString(str: worker, suffix: "_p");
838 }
839 if (field->is_repeated() && HasSuffixString(str: worker, suffix: "Array")) {
840 worker = StripSuffixString(str: worker, suffix: "Array");
841 }
842 if (field->type() == FieldDescriptor::TYPE_GROUP) {
843 if (worker.length() > 0) {
844 if (ascii_islower(c: worker[0])) {
845 worker[0] = ascii_toupper(c: worker[0]);
846 }
847 }
848 return worker;
849 } else {
850 std::string result;
851 for (int i = 0; i < worker.size(); i++) {
852 char c = worker[i];
853 if (ascii_isupper(c)) {
854 if (i > 0) {
855 result += '_';
856 }
857 result += ascii_tolower(c);
858 } else {
859 result += c;
860 }
861 }
862 return result;
863 }
864}
865
866std::string GetCapitalizedType(const FieldDescriptor* field) {
867 switch (field->type()) {
868 case FieldDescriptor::TYPE_INT32:
869 return "Int32";
870 case FieldDescriptor::TYPE_UINT32:
871 return "UInt32";
872 case FieldDescriptor::TYPE_SINT32:
873 return "SInt32";
874 case FieldDescriptor::TYPE_FIXED32:
875 return "Fixed32";
876 case FieldDescriptor::TYPE_SFIXED32:
877 return "SFixed32";
878 case FieldDescriptor::TYPE_INT64:
879 return "Int64";
880 case FieldDescriptor::TYPE_UINT64:
881 return "UInt64";
882 case FieldDescriptor::TYPE_SINT64:
883 return "SInt64";
884 case FieldDescriptor::TYPE_FIXED64:
885 return "Fixed64";
886 case FieldDescriptor::TYPE_SFIXED64:
887 return "SFixed64";
888 case FieldDescriptor::TYPE_FLOAT:
889 return "Float";
890 case FieldDescriptor::TYPE_DOUBLE:
891 return "Double";
892 case FieldDescriptor::TYPE_BOOL:
893 return "Bool";
894 case FieldDescriptor::TYPE_STRING:
895 return "String";
896 case FieldDescriptor::TYPE_BYTES:
897 return "Bytes";
898 case FieldDescriptor::TYPE_ENUM:
899 return "Enum";
900 case FieldDescriptor::TYPE_GROUP:
901 return "Group";
902 case FieldDescriptor::TYPE_MESSAGE:
903 return "Message";
904 }
905
906 // Some compilers report reaching end of function even though all cases of
907 // the enum are handed in the switch.
908 GOOGLE_LOG(FATAL) << "Can't get here.";
909 return std::string();
910}
911
912ObjectiveCType GetObjectiveCType(FieldDescriptor::Type field_type) {
913 switch (field_type) {
914 case FieldDescriptor::TYPE_INT32:
915 case FieldDescriptor::TYPE_SINT32:
916 case FieldDescriptor::TYPE_SFIXED32:
917 return OBJECTIVECTYPE_INT32;
918
919 case FieldDescriptor::TYPE_UINT32:
920 case FieldDescriptor::TYPE_FIXED32:
921 return OBJECTIVECTYPE_UINT32;
922
923 case FieldDescriptor::TYPE_INT64:
924 case FieldDescriptor::TYPE_SINT64:
925 case FieldDescriptor::TYPE_SFIXED64:
926 return OBJECTIVECTYPE_INT64;
927
928 case FieldDescriptor::TYPE_UINT64:
929 case FieldDescriptor::TYPE_FIXED64:
930 return OBJECTIVECTYPE_UINT64;
931
932 case FieldDescriptor::TYPE_FLOAT:
933 return OBJECTIVECTYPE_FLOAT;
934
935 case FieldDescriptor::TYPE_DOUBLE:
936 return OBJECTIVECTYPE_DOUBLE;
937
938 case FieldDescriptor::TYPE_BOOL:
939 return OBJECTIVECTYPE_BOOLEAN;
940
941 case FieldDescriptor::TYPE_STRING:
942 return OBJECTIVECTYPE_STRING;
943
944 case FieldDescriptor::TYPE_BYTES:
945 return OBJECTIVECTYPE_DATA;
946
947 case FieldDescriptor::TYPE_ENUM:
948 return OBJECTIVECTYPE_ENUM;
949
950 case FieldDescriptor::TYPE_GROUP:
951 case FieldDescriptor::TYPE_MESSAGE:
952 return OBJECTIVECTYPE_MESSAGE;
953 }
954
955 // Some compilers report reaching end of function even though all cases of
956 // the enum are handed in the switch.
957 GOOGLE_LOG(FATAL) << "Can't get here.";
958 return OBJECTIVECTYPE_INT32;
959}
960
961bool IsPrimitiveType(const FieldDescriptor* field) {
962 ObjectiveCType type = GetObjectiveCType(field);
963 switch (type) {
964 case OBJECTIVECTYPE_INT32:
965 case OBJECTIVECTYPE_UINT32:
966 case OBJECTIVECTYPE_INT64:
967 case OBJECTIVECTYPE_UINT64:
968 case OBJECTIVECTYPE_FLOAT:
969 case OBJECTIVECTYPE_DOUBLE:
970 case OBJECTIVECTYPE_BOOLEAN:
971 case OBJECTIVECTYPE_ENUM:
972 return true;
973 break;
974 default:
975 return false;
976 }
977}
978
979bool IsReferenceType(const FieldDescriptor* field) {
980 return !IsPrimitiveType(field);
981}
982
983static std::string HandleExtremeFloatingPoint(std::string val,
984 bool add_float_suffix) {
985 if (val == "nan") {
986 return "NAN";
987 } else if (val == "inf") {
988 return "INFINITY";
989 } else if (val == "-inf") {
990 return "-INFINITY";
991 } else {
992 // float strings with ., e or E need to have f appended
993 if (add_float_suffix && (val.find(s: ".") != std::string::npos ||
994 val.find(s: "e") != std::string::npos ||
995 val.find(s: "E") != std::string::npos)) {
996 val += "f";
997 }
998 return val;
999 }
1000}
1001
1002std::string GPBGenericValueFieldName(const FieldDescriptor* field) {
1003 // Returns the field within the GPBGenericValue union to use for the given
1004 // field.
1005 if (field->is_repeated()) {
1006 return "valueMessage";
1007 }
1008 switch (field->cpp_type()) {
1009 case FieldDescriptor::CPPTYPE_INT32:
1010 return "valueInt32";
1011 case FieldDescriptor::CPPTYPE_UINT32:
1012 return "valueUInt32";
1013 case FieldDescriptor::CPPTYPE_INT64:
1014 return "valueInt64";
1015 case FieldDescriptor::CPPTYPE_UINT64:
1016 return "valueUInt64";
1017 case FieldDescriptor::CPPTYPE_FLOAT:
1018 return "valueFloat";
1019 case FieldDescriptor::CPPTYPE_DOUBLE:
1020 return "valueDouble";
1021 case FieldDescriptor::CPPTYPE_BOOL:
1022 return "valueBool";
1023 case FieldDescriptor::CPPTYPE_STRING:
1024 if (field->type() == FieldDescriptor::TYPE_BYTES) {
1025 return "valueData";
1026 } else {
1027 return "valueString";
1028 }
1029 case FieldDescriptor::CPPTYPE_ENUM:
1030 return "valueEnum";
1031 case FieldDescriptor::CPPTYPE_MESSAGE:
1032 return "valueMessage";
1033 }
1034
1035 // Some compilers report reaching end of function even though all cases of
1036 // the enum are handed in the switch.
1037 GOOGLE_LOG(FATAL) << "Can't get here.";
1038 return std::string();
1039}
1040
1041
1042std::string DefaultValue(const FieldDescriptor* field) {
1043 // Repeated fields don't have defaults.
1044 if (field->is_repeated()) {
1045 return "nil";
1046 }
1047
1048 // Switch on cpp_type since we need to know which default_value_* method
1049 // of FieldDescriptor to call.
1050 switch (field->cpp_type()) {
1051 case FieldDescriptor::CPPTYPE_INT32:
1052 // gcc and llvm reject the decimal form of kint32min and kint64min.
1053 if (field->default_value_int32() == INT_MIN) {
1054 return "-0x80000000";
1055 }
1056 return StrCat(a: field->default_value_int32());
1057 case FieldDescriptor::CPPTYPE_UINT32:
1058 return StrCat(a: field->default_value_uint32()) + "U";
1059 case FieldDescriptor::CPPTYPE_INT64:
1060 // gcc and llvm reject the decimal form of kint32min and kint64min.
1061 if (field->default_value_int64() == LLONG_MIN) {
1062 return "-0x8000000000000000LL";
1063 }
1064 return StrCat(a: field->default_value_int64()) + "LL";
1065 case FieldDescriptor::CPPTYPE_UINT64:
1066 return StrCat(a: field->default_value_uint64()) + "ULL";
1067 case FieldDescriptor::CPPTYPE_DOUBLE:
1068 return HandleExtremeFloatingPoint(
1069 val: SimpleDtoa(value: field->default_value_double()), add_float_suffix: false);
1070 case FieldDescriptor::CPPTYPE_FLOAT:
1071 return HandleExtremeFloatingPoint(
1072 val: SimpleFtoa(value: field->default_value_float()), add_float_suffix: true);
1073 case FieldDescriptor::CPPTYPE_BOOL:
1074 return field->default_value_bool() ? "YES" : "NO";
1075 case FieldDescriptor::CPPTYPE_STRING: {
1076 const bool has_default_value = field->has_default_value();
1077 const std::string& default_string = field->default_value_string();
1078 if (!has_default_value || default_string.length() == 0) {
1079 // If the field is defined as being the empty string,
1080 // then we will just assign to nil, as the empty string is the
1081 // default for both strings and data.
1082 return "nil";
1083 }
1084 if (field->type() == FieldDescriptor::TYPE_BYTES) {
1085 // We want constant fields in our data structures so we can
1086 // declare them as static. To achieve this we cheat and stuff
1087 // a escaped c string (prefixed with a length) into the data
1088 // field, and cast it to an (NSData*) so it will compile.
1089 // The runtime library knows how to handle it.
1090
1091 // Must convert to a standard byte order for packing length into
1092 // a cstring.
1093 uint32_t length = ghtonl(x: default_string.length());
1094 std::string bytes((const char*)&length, sizeof(length));
1095 bytes.append(str: default_string);
1096 return "(NSData*)\"" + EscapeTrigraphs(to_escape: CEscape(src: bytes)) + "\"";
1097 } else {
1098 return "@\"" + EscapeTrigraphs(to_escape: CEscape(src: default_string)) + "\"";
1099 }
1100 }
1101 case FieldDescriptor::CPPTYPE_ENUM:
1102 return EnumValueName(descriptor: field->default_value_enum());
1103 case FieldDescriptor::CPPTYPE_MESSAGE:
1104 return "nil";
1105 }
1106
1107 // Some compilers report reaching end of function even though all cases of
1108 // the enum are handed in the switch.
1109 GOOGLE_LOG(FATAL) << "Can't get here.";
1110 return std::string();
1111}
1112
1113bool HasNonZeroDefaultValue(const FieldDescriptor* field) {
1114 // Repeated fields don't have defaults.
1115 if (field->is_repeated()) {
1116 return false;
1117 }
1118
1119 // As much as checking field->has_default_value() seems useful, it isn't
1120 // because of enums. proto2 syntax allows the first item in an enum (the
1121 // default) to be non zero. So checking field->has_default_value() would
1122 // result in missing this non zero default. See MessageWithOneBasedEnum in
1123 // objectivec/Tests/unittest_objc.proto for a test Message to confirm this.
1124
1125 // Some proto file set the default to the zero value, so make sure the value
1126 // isn't the zero case.
1127 switch (field->cpp_type()) {
1128 case FieldDescriptor::CPPTYPE_INT32:
1129 return field->default_value_int32() != 0;
1130 case FieldDescriptor::CPPTYPE_UINT32:
1131 return field->default_value_uint32() != 0U;
1132 case FieldDescriptor::CPPTYPE_INT64:
1133 return field->default_value_int64() != 0LL;
1134 case FieldDescriptor::CPPTYPE_UINT64:
1135 return field->default_value_uint64() != 0ULL;
1136 case FieldDescriptor::CPPTYPE_DOUBLE:
1137 return field->default_value_double() != 0.0;
1138 case FieldDescriptor::CPPTYPE_FLOAT:
1139 return field->default_value_float() != 0.0f;
1140 case FieldDescriptor::CPPTYPE_BOOL:
1141 return field->default_value_bool();
1142 case FieldDescriptor::CPPTYPE_STRING: {
1143 const std::string& default_string = field->default_value_string();
1144 return default_string.length() != 0;
1145 }
1146 case FieldDescriptor::CPPTYPE_ENUM:
1147 return field->default_value_enum()->number() != 0;
1148 case FieldDescriptor::CPPTYPE_MESSAGE:
1149 return false;
1150 }
1151
1152 // Some compilers report reaching end of function even though all cases of
1153 // the enum are handed in the switch.
1154 GOOGLE_LOG(FATAL) << "Can't get here.";
1155 return false;
1156}
1157
1158std::string BuildFlagsString(const FlagType flag_type,
1159 const std::vector<std::string>& strings) {
1160 if (strings.empty()) {
1161 return GetZeroEnumNameForFlagType(flag_type);
1162 } else if (strings.size() == 1) {
1163 return strings[0];
1164 }
1165 std::string string("(" + GetEnumNameForFlagType(flag_type) + ")(");
1166 for (size_t i = 0; i != strings.size(); ++i) {
1167 if (i > 0) {
1168 string.append(s: " | ");
1169 }
1170 string.append(str: strings[i]);
1171 }
1172 string.append(s: ")");
1173 return string;
1174}
1175
1176std::string BuildCommentsString(const SourceLocation& location,
1177 bool prefer_single_line) {
1178 const std::string& comments = location.leading_comments.empty()
1179 ? location.trailing_comments
1180 : location.leading_comments;
1181 std::vector<std::string> lines;
1182 lines = Split(full: comments, delim: "\n", skip_empty: false);
1183 while (!lines.empty() && lines.back().empty()) {
1184 lines.pop_back();
1185 }
1186 // If there are no comments, just return an empty string.
1187 if (lines.empty()) {
1188 return "";
1189 }
1190
1191 std::string prefix;
1192 std::string suffix;
1193 std::string final_comments;
1194 std::string epilogue;
1195
1196 bool add_leading_space = false;
1197
1198 if (prefer_single_line && lines.size() == 1) {
1199 prefix = "/** ";
1200 suffix = " */\n";
1201 } else {
1202 prefix = "* ";
1203 suffix = "\n";
1204 final_comments += "/**\n";
1205 epilogue = " **/\n";
1206 add_leading_space = true;
1207 }
1208
1209 for (int i = 0; i < lines.size(); i++) {
1210 std::string line = StripPrefixString(str: lines[i], prefix: " ");
1211 // HeaderDoc and appledoc use '\' and '@' for markers; escape them.
1212 line = StringReplace(s: line, oldsub: "\\", newsub: "\\\\", replace_all: true);
1213 line = StringReplace(s: line, oldsub: "@", newsub: "\\@", replace_all: true);
1214 // Decouple / from * to not have inline comments inside comments.
1215 line = StringReplace(s: line, oldsub: "/*", newsub: "/\\*", replace_all: true);
1216 line = StringReplace(s: line, oldsub: "*/", newsub: "*\\/", replace_all: true);
1217 line = prefix + line;
1218 StripWhitespace(s: &line);
1219 // If not a one line, need to add the first space before *, as
1220 // StripWhitespace would have removed it.
1221 line = (add_leading_space ? " " : "") + line;
1222 final_comments += line + suffix;
1223 }
1224 final_comments += epilogue;
1225 return final_comments;
1226}
1227
1228// Making these a generator option for folks that don't use CocoaPods, but do
1229// want to put the library in a framework is an interesting question. The
1230// problem is it means changing sources shipped with the library to actually
1231// use a different value; so it isn't as simple as a option.
1232const char* const ProtobufLibraryFrameworkName = "Protobuf";
1233
1234std::string ProtobufFrameworkImportSymbol(const std::string& framework_name) {
1235 // GPB_USE_[framework_name]_FRAMEWORK_IMPORTS
1236 std::string result = std::string("GPB_USE_");
1237 result += ToUpper(s: framework_name);
1238 result += "_FRAMEWORK_IMPORTS";
1239 return result;
1240}
1241
1242bool IsProtobufLibraryBundledProtoFile(const FileDescriptor* file) {
1243 // We don't check the name prefix or proto package because some files
1244 // (descriptor.proto), aren't shipped generated by the library, so this
1245 // seems to be the safest way to only catch the ones shipped.
1246 const std::string name = file->name();
1247 if (name == "google/protobuf/any.proto" ||
1248 name == "google/protobuf/api.proto" ||
1249 name == "google/protobuf/duration.proto" ||
1250 name == "google/protobuf/empty.proto" ||
1251 name == "google/protobuf/field_mask.proto" ||
1252 name == "google/protobuf/source_context.proto" ||
1253 name == "google/protobuf/struct.proto" ||
1254 name == "google/protobuf/timestamp.proto" ||
1255 name == "google/protobuf/type.proto" ||
1256 name == "google/protobuf/wrappers.proto") {
1257 return true;
1258 }
1259 return false;
1260}
1261
1262bool ReadLine(StringPiece* input, StringPiece* line) {
1263 for (int len = 0; len < input->size(); ++len) {
1264 if (ascii_isnewline(c: (*input)[len])) {
1265 *line = StringPiece(input->data(), len);
1266 ++len; // advance over the newline
1267 *input = StringPiece(input->data() + len, input->size() - len);
1268 return true;
1269 }
1270 }
1271 return false; // Ran out of input with no newline.
1272}
1273
1274void RemoveComment(StringPiece* input) {
1275 int offset = input->find(c: '#');
1276 if (offset != StringPiece::npos) {
1277 input->remove_suffix(n: input->length() - offset);
1278 }
1279}
1280
1281namespace {
1282
1283bool PackageToPrefixesCollector::ConsumeLine(
1284 const StringPiece& line, std::string* out_error) {
1285 int offset = line.find(c: '=');
1286 if (offset == StringPiece::npos) {
1287 *out_error = usage_ + " file line without equal sign: '" + StrCat(a: line) + "'.";
1288 return false;
1289 }
1290 StringPiece package = line.substr(pos: 0, n: offset);
1291 StringPiece prefix = line.substr(pos: offset + 1);
1292 TrimWhitespace(input: &package);
1293 TrimWhitespace(input: &prefix);
1294 MaybeUnQuote(input: &prefix);
1295 // Don't really worry about error checking the package/prefix for
1296 // being valid. Assume the file is validated when it is created/edited.
1297 (*prefix_map_)[std::string(package)] = std::string(prefix);
1298 return true;
1299}
1300
1301bool LoadExpectedPackagePrefixes(const std::string& expected_prefixes_path,
1302 std::map<std::string, std::string>* prefix_map,
1303 std::string* out_error) {
1304 if (expected_prefixes_path.empty()) {
1305 return true;
1306 }
1307
1308 PackageToPrefixesCollector collector("Expected prefixes", prefix_map);
1309 return ParseSimpleFile(
1310 path: expected_prefixes_path, line_consumer: &collector, out_error);
1311}
1312
1313bool ValidateObjCClassPrefix(
1314 const FileDescriptor* file, const std::string& expected_prefixes_path,
1315 const std::map<std::string, std::string>& expected_package_prefixes,
1316 bool prefixes_must_be_registered, bool require_prefixes,
1317 std::string* out_error) {
1318 // Reminder: An explicit prefix option of "" is valid in case the default
1319 // prefixing is set to use the proto package and a file needs to be generated
1320 // without any prefix at all (for legacy reasons).
1321
1322 bool has_prefix = file->options().has_objc_class_prefix();
1323 bool have_expected_prefix_file = !expected_prefixes_path.empty();
1324
1325 const std::string prefix = file->options().objc_class_prefix();
1326 const std::string package = file->package();
1327 // For files without packages, the can be registered as "no_package:PATH",
1328 // allowing the expected prefixes file.
1329 static const std::string no_package_prefix("no_package:");
1330 const std::string lookup_key =
1331 package.empty() ? no_package_prefix + file->name() : package;
1332
1333 // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
1334 // error cases, so it seems to be ok to use as a back door for warnings.
1335
1336 // Check: Error - See if there was an expected prefix for the package and
1337 // report if it doesn't match (wrong or missing).
1338 std::map<std::string, std::string>::const_iterator package_match =
1339 expected_package_prefixes.find(x: lookup_key);
1340 if (package_match != expected_package_prefixes.end()) {
1341 // There was an entry, and...
1342 if (has_prefix && package_match->second == prefix) {
1343 // ...it matches. All good, out of here!
1344 return true;
1345 } else {
1346 // ...it didn't match!
1347 *out_error = "error: Expected 'option objc_class_prefix = \"" +
1348 package_match->second + "\";'";
1349 if (!package.empty()) {
1350 *out_error += " for package '" + package + "'";
1351 }
1352 *out_error += " in '" + file->name() + "'";
1353 if (has_prefix) {
1354 *out_error += "; but found '" + prefix + "' instead";
1355 }
1356 *out_error += ".";
1357 return false;
1358 }
1359 }
1360
1361 // If there was no prefix option, we're done at this point.
1362 if (!has_prefix) {
1363 if (require_prefixes) {
1364 *out_error =
1365 "error: '" + file->name() + "' does not have a required 'option" +
1366 " objc_class_prefix'.";
1367 return false;
1368 }
1369 return true;
1370 }
1371
1372 // When the prefix is non empty, check it against the expected entries.
1373 if (!prefix.empty() && have_expected_prefix_file) {
1374 // For a non empty prefix, look for any other package that uses the prefix.
1375 std::string other_package_for_prefix;
1376 for (std::map<std::string, std::string>::const_iterator i =
1377 expected_package_prefixes.begin();
1378 i != expected_package_prefixes.end(); ++i) {
1379 if (i->second == prefix) {
1380 other_package_for_prefix = i->first;
1381 // Stop on the first real package listing, if it was a no_package file
1382 // specific entry, keep looking to try and find a package one.
1383 if (!HasPrefixString(str: other_package_for_prefix, prefix: no_package_prefix)) {
1384 break;
1385 }
1386 }
1387 }
1388
1389 // Check: Error - Make sure the prefix wasn't expected for a different
1390 // package (overlap is allowed, but it has to be listed as an expected
1391 // overlap).
1392 if (!other_package_for_prefix.empty()) {
1393 *out_error =
1394 "error: Found 'option objc_class_prefix = \"" + prefix +
1395 "\";' in '" + file->name() + "'; that prefix is already used for ";
1396 if (HasPrefixString(str: other_package_for_prefix, prefix: no_package_prefix)) {
1397 *out_error += "file '" +
1398 StripPrefixString(str: other_package_for_prefix, prefix: no_package_prefix) +
1399 "'.";
1400 } else {
1401 *out_error += "'package " + other_package_for_prefix + ";'.";
1402 }
1403 *out_error +=
1404 " It can only be reused by adding '" + lookup_key + " = " + prefix +
1405 "' to the expected prefixes file (" + expected_prefixes_path + ").";
1406 return false; // Only report first usage of the prefix.
1407 }
1408 } // !prefix.empty() && have_expected_prefix_file
1409
1410 // Check: Warning - Make sure the prefix is is a reasonable value according
1411 // to Apple's rules (the checks above implicitly whitelist anything that
1412 // doesn't meet these rules).
1413 if (!prefix.empty() && !ascii_isupper(c: prefix[0])) {
1414 std::cerr
1415 << "protoc:0: warning: Invalid 'option objc_class_prefix = \""
1416 << prefix << "\";' in '" << file->name() << "';"
1417 << " it should start with a capital letter." << std::endl;
1418 std::cerr.flush();
1419 }
1420 if (!prefix.empty() && prefix.length() < 3) {
1421 // Apple reserves 2 character prefixes for themselves. They do use some
1422 // 3 character prefixes, but they haven't updated the rules/docs.
1423 std::cerr
1424 << "protoc:0: warning: Invalid 'option objc_class_prefix = \""
1425 << prefix << "\";' in '" << file->name() << "';"
1426 << " Apple recommends they should be at least 3 characters long."
1427 << std::endl;
1428 std::cerr.flush();
1429 }
1430
1431 // Check: Error/Warning - If the given package/prefix pair wasn't expected,
1432 // issue a error/warning to added to the file.
1433 if (have_expected_prefix_file) {
1434 if (prefixes_must_be_registered) {
1435 *out_error =
1436 "error: '" + file->name() + "' has 'option objc_class_prefix = \"" +
1437 prefix + "\";', but it is not registered. Add '" + lookup_key + " = " +
1438 (prefix.empty() ? "\"\"" : prefix) +
1439 "' to the expected prefixes file (" + expected_prefixes_path + ").";
1440 return false;
1441 }
1442
1443 std::cerr
1444 << "protoc:0: warning: Found unexpected 'option objc_class_prefix = \""
1445 << prefix << "\";' in '" << file->name() << "'; consider adding '"
1446 << lookup_key << " = " << (prefix.empty() ? "\"\"" : prefix)
1447 << "' to the expected prefixes file (" << expected_prefixes_path
1448 << ")." << std::endl;
1449 std::cerr.flush();
1450 }
1451
1452 return true;
1453}
1454
1455} // namespace
1456
1457bool ValidateObjCClassPrefixes(const std::vector<const FileDescriptor*>& files,
1458 std::string* out_error) {
1459 // Options's ctor load from the environment.
1460 Options options;
1461 return ValidateObjCClassPrefixes(files, validation_options: options, out_error);
1462}
1463
1464bool ValidateObjCClassPrefixes(const std::vector<const FileDescriptor*>& files,
1465 const Options& generation_options,
1466 std::string* out_error) {
1467 // Allow a '-' as the path for the expected prefixes to completely disable
1468 // even the most basic of checks.
1469 if (generation_options.expected_prefixes_path == "-") {
1470 return true;
1471 }
1472
1473 // Load the expected package prefixes, if available, to validate against.
1474 std::map<std::string, std::string> expected_package_prefixes;
1475 if (!LoadExpectedPackagePrefixes(expected_prefixes_path: generation_options.expected_prefixes_path,
1476 prefix_map: &expected_package_prefixes,
1477 out_error)) {
1478 return false;
1479 }
1480
1481 for (int i = 0; i < files.size(); i++) {
1482 bool should_skip =
1483 (std::find(first: generation_options.expected_prefixes_suppressions.begin(),
1484 last: generation_options.expected_prefixes_suppressions.end(),
1485 val: files[i]->name())
1486 != generation_options.expected_prefixes_suppressions.end());
1487 if (should_skip) {
1488 continue;
1489 }
1490
1491 bool is_valid =
1492 ValidateObjCClassPrefix(file: files[i],
1493 expected_prefixes_path: generation_options.expected_prefixes_path,
1494 expected_package_prefixes,
1495 prefixes_must_be_registered: generation_options.prefixes_must_be_registered,
1496 require_prefixes: generation_options.require_prefixes,
1497 out_error);
1498 if (!is_valid) {
1499 return false;
1500 }
1501 }
1502 return true;
1503}
1504
1505TextFormatDecodeData::TextFormatDecodeData() { }
1506
1507TextFormatDecodeData::~TextFormatDecodeData() { }
1508
1509void TextFormatDecodeData::AddString(int32_t key,
1510 const std::string& input_for_decode,
1511 const std::string& desired_output) {
1512 for (std::vector<DataEntry>::const_iterator i = entries_.begin();
1513 i != entries_.end(); ++i) {
1514 if (i->first == key) {
1515 std::cerr << "error: duplicate key (" << key
1516 << ") making TextFormat data, input: \"" << input_for_decode
1517 << "\", desired: \"" << desired_output << "\"." << std::endl;
1518 std::cerr.flush();
1519 abort();
1520 }
1521 }
1522
1523 const std::string& data = TextFormatDecodeData::DecodeDataForString(
1524 input_for_decode, desired_output);
1525 entries_.push_back(x: DataEntry(key, data));
1526}
1527
1528std::string TextFormatDecodeData::Data() const {
1529 std::ostringstream data_stringstream;
1530
1531 if (num_entries() > 0) {
1532 io::OstreamOutputStream data_outputstream(&data_stringstream);
1533 io::CodedOutputStream output_stream(&data_outputstream);
1534
1535 output_stream.WriteVarint32(value: num_entries());
1536 for (std::vector<DataEntry>::const_iterator i = entries_.begin();
1537 i != entries_.end(); ++i) {
1538 output_stream.WriteVarint32(value: i->first);
1539 output_stream.WriteString(str: i->second);
1540 }
1541 }
1542
1543 data_stringstream.flush();
1544 return data_stringstream.str();
1545}
1546
1547namespace {
1548
1549// Helper to build up the decode data for a string.
1550class DecodeDataBuilder {
1551 public:
1552 DecodeDataBuilder() { Reset(); }
1553
1554 bool AddCharacter(const char desired, const char input);
1555 void AddUnderscore() {
1556 Push();
1557 need_underscore_ = true;
1558 }
1559 std::string Finish() {
1560 Push();
1561 return decode_data_;
1562 }
1563
1564 private:
1565 static constexpr uint8_t kAddUnderscore = 0x80;
1566
1567 static constexpr uint8_t kOpAsIs = 0x00;
1568 static constexpr uint8_t kOpFirstUpper = 0x40;
1569 static constexpr uint8_t kOpFirstLower = 0x20;
1570 static constexpr uint8_t kOpAllUpper = 0x60;
1571
1572 static constexpr int kMaxSegmentLen = 0x1f;
1573
1574 void AddChar(const char desired) {
1575 ++segment_len_;
1576 is_all_upper_ &= ascii_isupper(c: desired);
1577 }
1578
1579 void Push() {
1580 uint8_t op = (op_ | segment_len_);
1581 if (need_underscore_) op |= kAddUnderscore;
1582 if (op != 0) {
1583 decode_data_ += (char)op;
1584 }
1585 Reset();
1586 }
1587
1588 bool AddFirst(const char desired, const char input) {
1589 if (desired == input) {
1590 op_ = kOpAsIs;
1591 } else if (desired == ascii_toupper(c: input)) {
1592 op_ = kOpFirstUpper;
1593 } else if (desired == ascii_tolower(c: input)) {
1594 op_ = kOpFirstLower;
1595 } else {
1596 // Can't be transformed to match.
1597 return false;
1598 }
1599 AddChar(desired);
1600 return true;
1601 }
1602
1603 void Reset() {
1604 need_underscore_ = false;
1605 op_ = 0;
1606 segment_len_ = 0;
1607 is_all_upper_ = true;
1608 }
1609
1610 bool need_underscore_;
1611 bool is_all_upper_;
1612 uint8_t op_;
1613 int segment_len_;
1614
1615 std::string decode_data_;
1616};
1617
1618bool DecodeDataBuilder::AddCharacter(const char desired, const char input) {
1619 // If we've hit the max size, push to start a new segment.
1620 if (segment_len_ == kMaxSegmentLen) {
1621 Push();
1622 }
1623 if (segment_len_ == 0) {
1624 return AddFirst(desired, input);
1625 }
1626
1627 // Desired and input match...
1628 if (desired == input) {
1629 // If we aren't transforming it, or we're upper casing it and it is
1630 // supposed to be uppercase; just add it to the segment.
1631 if ((op_ != kOpAllUpper) || ascii_isupper(c: desired)) {
1632 AddChar(desired);
1633 return true;
1634 }
1635
1636 // Add the current segment, and start the next one.
1637 Push();
1638 return AddFirst(desired, input);
1639 }
1640
1641 // If we need to uppercase, and everything so far has been uppercase,
1642 // promote op to AllUpper.
1643 if ((desired == ascii_toupper(c: input)) && is_all_upper_) {
1644 op_ = kOpAllUpper;
1645 AddChar(desired);
1646 return true;
1647 }
1648
1649 // Give up, push and start a new segment.
1650 Push();
1651 return AddFirst(desired, input);
1652}
1653
1654// If decode data can't be generated, a directive for the raw string
1655// is used instead.
1656std::string DirectDecodeString(const std::string& str) {
1657 std::string result;
1658 result += (char)'\0'; // Marker for full string.
1659 result += str;
1660 result += (char)'\0'; // End of string.
1661 return result;
1662}
1663
1664} // namespace
1665
1666// static
1667std::string TextFormatDecodeData::DecodeDataForString(
1668 const std::string& input_for_decode, const std::string& desired_output) {
1669 if (input_for_decode.empty() || desired_output.empty()) {
1670 std::cerr << "error: got empty string for making TextFormat data, input: \""
1671 << input_for_decode << "\", desired: \"" << desired_output << "\"."
1672 << std::endl;
1673 std::cerr.flush();
1674 abort();
1675 }
1676 if ((input_for_decode.find(c: '\0') != std::string::npos) ||
1677 (desired_output.find(c: '\0') != std::string::npos)) {
1678 std::cerr << "error: got a null char in a string for making TextFormat data,"
1679 << " input: \"" << CEscape(src: input_for_decode) << "\", desired: \""
1680 << CEscape(src: desired_output) << "\"." << std::endl;
1681 std::cerr.flush();
1682 abort();
1683 }
1684
1685 DecodeDataBuilder builder;
1686
1687 // Walk the output building it from the input.
1688 int x = 0;
1689 for (int y = 0; y < desired_output.size(); y++) {
1690 const char d = desired_output[y];
1691 if (d == '_') {
1692 builder.AddUnderscore();
1693 continue;
1694 }
1695
1696 if (x >= input_for_decode.size()) {
1697 // Out of input, no way to encode it, just return a full decode.
1698 return DirectDecodeString(str: desired_output);
1699 }
1700 if (builder.AddCharacter(desired: d, input: input_for_decode[x])) {
1701 ++x; // Consumed one input
1702 } else {
1703 // Couldn't transform for the next character, just return a full decode.
1704 return DirectDecodeString(str: desired_output);
1705 }
1706 }
1707
1708 if (x != input_for_decode.size()) {
1709 // Extra input (suffix from name sanitizing?), just return a full decode.
1710 return DirectDecodeString(str: desired_output);
1711 }
1712
1713 // Add the end marker.
1714 return builder.Finish() + (char)'\0';
1715}
1716
1717namespace {
1718
1719class Parser {
1720 public:
1721 Parser(LineConsumer* line_consumer)
1722 : line_consumer_(line_consumer), line_(0) {}
1723
1724 // Feeds in some input, parse what it can, returning success/failure. Calling
1725 // again after an error is undefined.
1726 bool ParseChunk(StringPiece chunk, std::string* out_error);
1727
1728 // Should be called to finish parsing (after all input has been provided via
1729 // successful calls to ParseChunk(), calling after a ParseChunk() failure is
1730 // undefined). Returns success/failure.
1731 bool Finish(std::string* out_error);
1732
1733 int last_line() const { return line_; }
1734
1735 private:
1736 LineConsumer* line_consumer_;
1737 int line_;
1738 std::string leftover_;
1739};
1740
1741bool Parser::ParseChunk(StringPiece chunk, std::string* out_error) {
1742 StringPiece full_chunk;
1743 if (!leftover_.empty()) {
1744 leftover_ += std::string(chunk);
1745 full_chunk = StringPiece(leftover_);
1746 } else {
1747 full_chunk = chunk;
1748 }
1749
1750 StringPiece line;
1751 while (ReadLine(input: &full_chunk, line: &line)) {
1752 ++line_;
1753 RemoveComment(input: &line);
1754 TrimWhitespace(input: &line);
1755 if (!line.empty() && !line_consumer_->ConsumeLine(line, out_error)) {
1756 if (out_error->empty()) {
1757 *out_error = "ConsumeLine failed without setting an error.";
1758 }
1759 leftover_.clear();
1760 return false;
1761 }
1762 }
1763
1764 if (full_chunk.empty()) {
1765 leftover_.clear();
1766 } else {
1767 leftover_ = std::string(full_chunk);
1768 }
1769 return true;
1770}
1771
1772bool Parser::Finish(std::string* out_error) {
1773 // If there is still something to go, flush it with a newline.
1774 if (!leftover_.empty() && !ParseChunk(chunk: "\n", out_error)) {
1775 return false;
1776 }
1777 // This really should never fail if ParseChunk succeeded, but check to be sure.
1778 if (!leftover_.empty()) {
1779 *out_error = "ParseSimple Internal error: finished with pending data.";
1780 return false;
1781 }
1782 return true;
1783}
1784
1785std::string FullErrorString(const std::string& name, int line_num, const std::string& msg) {
1786 return std::string("error: ") + name + " Line " + StrCat(a: line_num) + ", " + msg;
1787}
1788
1789} // namespace
1790
1791LineConsumer::LineConsumer() {}
1792
1793LineConsumer::~LineConsumer() {}
1794
1795bool ParseSimpleFile(const std::string& path, LineConsumer* line_consumer,
1796 std::string* out_error) {
1797 int fd;
1798 do {
1799 fd = posix::open(file: path.c_str(), O_RDONLY);
1800 } while (fd < 0 && errno == EINTR);
1801 if (fd < 0) {
1802 *out_error = std::string("error: Unable to open \"") + path + "\", " +
1803 strerror(errno);
1804 return false;
1805 }
1806 io::FileInputStream file_stream(fd);
1807 file_stream.SetCloseOnDelete(true);
1808
1809 return ParseSimpleStream(input_stream&: file_stream, stream_name: path, line_consumer, out_error);
1810}
1811
1812bool ParseSimpleStream(io::ZeroCopyInputStream& input_stream,
1813 const std::string& stream_name,
1814 LineConsumer* line_consumer,
1815 std::string* out_error) {
1816 std::string local_error;
1817 Parser parser(line_consumer);
1818 const void* buf;
1819 int buf_len;
1820 while (input_stream.Next(data: &buf, size: &buf_len)) {
1821 if (buf_len == 0) {
1822 continue;
1823 }
1824
1825 if (!parser.ParseChunk(chunk: StringPiece(static_cast<const char*>(buf), buf_len),
1826 out_error: &local_error)) {
1827 *out_error = FullErrorString(name: stream_name, line_num: parser.last_line(), msg: local_error);
1828 return false;
1829 }
1830 }
1831 if (!parser.Finish(out_error: &local_error)) {
1832 *out_error = FullErrorString(name: stream_name, line_num: parser.last_line(), msg: local_error);
1833 return false;
1834 }
1835 return true;
1836}
1837
1838ImportWriter::ImportWriter(
1839 const std::string& generate_for_named_framework,
1840 const std::string& named_framework_to_proto_path_mappings_path,
1841 const std::string& runtime_import_prefix, bool include_wkt_imports)
1842 : generate_for_named_framework_(generate_for_named_framework),
1843 named_framework_to_proto_path_mappings_path_(
1844 named_framework_to_proto_path_mappings_path),
1845 runtime_import_prefix_(runtime_import_prefix),
1846 include_wkt_imports_(include_wkt_imports),
1847 need_to_parse_mapping_file_(true) {}
1848
1849ImportWriter::~ImportWriter() {}
1850
1851void ImportWriter::AddFile(const FileDescriptor* file,
1852 const std::string& header_extension) {
1853 if (IsProtobufLibraryBundledProtoFile(file)) {
1854 // The imports of the WKTs are only needed within the library itself,
1855 // in other cases, they get skipped because the generated code already
1856 // import GPBProtocolBuffers.h and hence proves them.
1857 if (include_wkt_imports_) {
1858 const std::string header_name =
1859 "GPB" + FilePathBasename(file) + header_extension;
1860 protobuf_imports_.push_back(x: header_name);
1861 }
1862 return;
1863 }
1864
1865 // Lazy parse any mappings.
1866 if (need_to_parse_mapping_file_) {
1867 ParseFrameworkMappings();
1868 }
1869
1870 std::map<std::string, std::string>::iterator proto_lookup =
1871 proto_file_to_framework_name_.find(x: file->name());
1872 if (proto_lookup != proto_file_to_framework_name_.end()) {
1873 other_framework_imports_.push_back(
1874 x: proto_lookup->second + "/" +
1875 FilePathBasename(file) + header_extension);
1876 return;
1877 }
1878
1879 if (!generate_for_named_framework_.empty()) {
1880 other_framework_imports_.push_back(
1881 x: generate_for_named_framework_ + "/" +
1882 FilePathBasename(file) + header_extension);
1883 return;
1884 }
1885
1886 other_imports_.push_back(x: FilePath(file) + header_extension);
1887}
1888
1889void ImportWriter::Print(io::Printer* printer) const {
1890 bool add_blank_line = false;
1891
1892 if (!protobuf_imports_.empty()) {
1893 PrintRuntimeImports(printer, header_to_import: protobuf_imports_, runtime_import_prefix: runtime_import_prefix_);
1894 add_blank_line = true;
1895 }
1896
1897 if (!other_framework_imports_.empty()) {
1898 if (add_blank_line) {
1899 printer->Print(text: "\n");
1900 }
1901
1902 for (std::vector<std::string>::const_iterator iter =
1903 other_framework_imports_.begin();
1904 iter != other_framework_imports_.end(); ++iter) {
1905 printer->Print(
1906 text: "#import <$header$>\n",
1907 args: "header", args: *iter);
1908 }
1909
1910 add_blank_line = true;
1911 }
1912
1913 if (!other_imports_.empty()) {
1914 if (add_blank_line) {
1915 printer->Print(text: "\n");
1916 }
1917
1918 for (std::vector<std::string>::const_iterator iter = other_imports_.begin();
1919 iter != other_imports_.end(); ++iter) {
1920 printer->Print(
1921 text: "#import \"$header$\"\n",
1922 args: "header", args: *iter);
1923 }
1924 }
1925}
1926
1927void ImportWriter::PrintRuntimeImports(
1928 io::Printer* printer, const std::vector<std::string>& header_to_import,
1929 const std::string& runtime_import_prefix, bool default_cpp_symbol) {
1930 // Given an override, use that.
1931 if (!runtime_import_prefix.empty()) {
1932 for (const auto& header : header_to_import) {
1933 printer->Print(
1934 text: " #import \"$import_prefix$/$header$\"\n",
1935 args: "import_prefix", args: runtime_import_prefix,
1936 args: "header", args: header);
1937 }
1938 return;
1939 }
1940
1941 const std::string framework_name(ProtobufLibraryFrameworkName);
1942 const std::string cpp_symbol(ProtobufFrameworkImportSymbol(framework_name));
1943
1944 if (default_cpp_symbol) {
1945 printer->Print(
1946 text: "// This CPP symbol can be defined to use imports that match up to the framework\n"
1947 "// imports needed when using CocoaPods.\n"
1948 "#if !defined($cpp_symbol$)\n"
1949 " #define $cpp_symbol$ 0\n"
1950 "#endif\n"
1951 "\n",
1952 args: "cpp_symbol", args: cpp_symbol);
1953 }
1954
1955 printer->Print(
1956 text: "#if $cpp_symbol$\n",
1957 args: "cpp_symbol", args: cpp_symbol);
1958 for (const auto& header : header_to_import) {
1959 printer->Print(
1960 text: " #import <$framework_name$/$header$>\n",
1961 args: "framework_name", args: framework_name,
1962 args: "header", args: header);
1963 }
1964 printer->Print(
1965 text: "#else\n");
1966 for (const auto& header : header_to_import) {
1967 printer->Print(
1968 text: " #import \"$header$\"\n",
1969 args: "header", args: header);
1970 }
1971 printer->Print(
1972 text: "#endif\n");
1973}
1974
1975void ImportWriter::ParseFrameworkMappings() {
1976 need_to_parse_mapping_file_ = false;
1977 if (named_framework_to_proto_path_mappings_path_.empty()) {
1978 return; // Nothing to do.
1979 }
1980
1981 ProtoFrameworkCollector collector(&proto_file_to_framework_name_);
1982 std::string parse_error;
1983 if (!ParseSimpleFile(path: named_framework_to_proto_path_mappings_path_,
1984 line_consumer: &collector, out_error: &parse_error)) {
1985 std::cerr << "error parsing " << named_framework_to_proto_path_mappings_path_
1986 << " : " << parse_error << std::endl;
1987 std::cerr.flush();
1988 }
1989}
1990
1991bool ImportWriter::ProtoFrameworkCollector::ConsumeLine(
1992 const StringPiece& line, std::string* out_error) {
1993 int offset = line.find(c: ':');
1994 if (offset == StringPiece::npos) {
1995 *out_error =
1996 std::string("Framework/proto file mapping line without colon sign: '") +
1997 std::string(line) + "'.";
1998 return false;
1999 }
2000 StringPiece framework_name = line.substr(pos: 0, n: offset);
2001 StringPiece proto_file_list = line.substr(pos: offset + 1);
2002 TrimWhitespace(input: &framework_name);
2003
2004 int start = 0;
2005 while (start < proto_file_list.length()) {
2006 offset = proto_file_list.find(c: ',', pos: start);
2007 if (offset == StringPiece::npos) {
2008 offset = proto_file_list.length();
2009 }
2010
2011 StringPiece proto_file = proto_file_list.substr(pos: start, n: offset - start);
2012 TrimWhitespace(input: &proto_file);
2013 if (!proto_file.empty()) {
2014 std::map<std::string, std::string>::iterator existing_entry =
2015 map_->find(x: std::string(proto_file));
2016 if (existing_entry != map_->end()) {
2017 std::cerr << "warning: duplicate proto file reference, replacing "
2018 "framework entry for '"
2019 << std::string(proto_file) << "' with '" << std::string(framework_name)
2020 << "' (was '" << existing_entry->second << "')." << std::endl;
2021 std::cerr.flush();
2022 }
2023
2024 if (proto_file.find(c: ' ') != StringPiece::npos) {
2025 std::cerr << "note: framework mapping file had a proto file with a "
2026 "space in, hopefully that isn't a missing comma: '"
2027 << std::string(proto_file) << "'" << std::endl;
2028 std::cerr.flush();
2029 }
2030
2031 (*map_)[std::string(proto_file)] = std::string(framework_name);
2032 }
2033
2034 start = offset + 1;
2035 }
2036
2037 return true;
2038}
2039
2040} // namespace objectivec
2041} // namespace compiler
2042} // namespace protobuf
2043} // namespace google
2044