|
|
|
@ -28,10 +28,18 @@
|
|
|
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
#include <io.h>
|
|
|
|
|
#else
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#endif
|
|
|
|
|
#include <climits>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include <google/protobuf/stubs/hash.h>
|
|
|
|
@ -39,6 +47,7 @@
|
|
|
|
|
#include <google/protobuf/io/coded_stream.h>
|
|
|
|
|
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
|
|
|
|
#include <google/protobuf/descriptor.pb.h>
|
|
|
|
|
#include <google/protobuf/stubs/common.h>
|
|
|
|
|
#include <google/protobuf/stubs/strutil.h>
|
|
|
|
|
|
|
|
|
|
// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
|
|
|
|
@ -51,45 +60,6 @@ namespace objectivec {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// islower()/isupper()/tolower()/toupper() change based on locale.
|
|
|
|
|
//
|
|
|
|
|
// src/google/protobuf/stubs/strutil.h:150 has the same pattern. For the
|
|
|
|
|
// Objective C plugin, test failures were seen on TravisCI because isupper('A')
|
|
|
|
|
// was coming back false for some server's locale. This approach avoids any
|
|
|
|
|
// such issues.
|
|
|
|
|
|
|
|
|
|
bool IsLower(const char c) {
|
|
|
|
|
return ('a' <= c && c <= 'z');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsUpper(const char c) {
|
|
|
|
|
return ('A' <= c && c <= 'Z');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char ToLower(char c) {
|
|
|
|
|
if ('A' <= c && c <= 'Z') {
|
|
|
|
|
c += 'a' - 'A';
|
|
|
|
|
}
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// toupper() changes based on locale. We don't want this!
|
|
|
|
|
char ToUpper(char c) {
|
|
|
|
|
if ('a' <= c && c <= 'z') {
|
|
|
|
|
c += 'A' - 'a';
|
|
|
|
|
}
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string TrimString(const string& s) {
|
|
|
|
|
string::size_type start = s.find_first_not_of(" \n\r\t");
|
|
|
|
|
if (start == string::npos) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
string::size_type end = s.find_last_not_of(" \n\r\t") + 1;
|
|
|
|
|
return s.substr(start, end - start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hash_set<string> MakeWordsMap(const char* const words[], size_t num_words) {
|
|
|
|
|
hash_set<string> result;
|
|
|
|
|
for (int i = 0; i < num_words; i++) {
|
|
|
|
@ -115,7 +85,7 @@ string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
|
|
|
|
|
bool last_char_was_upper = false;
|
|
|
|
|
for (int i = 0; i < input.size(); i++) {
|
|
|
|
|
char c = input[i];
|
|
|
|
|
if (c >= '0' && c <= '9') {
|
|
|
|
|
if (ascii_isdigit(c)) {
|
|
|
|
|
if (!last_char_was_number) {
|
|
|
|
|
values.push_back(current);
|
|
|
|
|
current = "";
|
|
|
|
@ -123,7 +93,7 @@ string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
|
|
|
|
|
current += c;
|
|
|
|
|
last_char_was_number = last_char_was_lower = last_char_was_upper = false;
|
|
|
|
|
last_char_was_number = true;
|
|
|
|
|
} else if (IsLower(c)) {
|
|
|
|
|
} else if (ascii_islower(c)) {
|
|
|
|
|
// lowercase letter can follow a lowercase or uppercase letter
|
|
|
|
|
if (!last_char_was_lower && !last_char_was_upper) {
|
|
|
|
|
values.push_back(current);
|
|
|
|
@ -132,12 +102,12 @@ string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
|
|
|
|
|
current += c; // already lower
|
|
|
|
|
last_char_was_number = last_char_was_lower = last_char_was_upper = false;
|
|
|
|
|
last_char_was_lower = true;
|
|
|
|
|
} else if (IsUpper(c)) {
|
|
|
|
|
} else if (ascii_isupper(c)) {
|
|
|
|
|
if (!last_char_was_upper) {
|
|
|
|
|
values.push_back(current);
|
|
|
|
|
current = "";
|
|
|
|
|
}
|
|
|
|
|
current += ToLower(c);
|
|
|
|
|
current += ascii_tolower(c);
|
|
|
|
|
last_char_was_number = last_char_was_lower = last_char_was_upper = false;
|
|
|
|
|
last_char_was_upper = true;
|
|
|
|
|
} else {
|
|
|
|
@ -151,7 +121,7 @@ string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
|
|
|
|
|
bool all_upper = (kUpperSegments.count(value) > 0);
|
|
|
|
|
for (int j = 0; j < value.length(); j++) {
|
|
|
|
|
if (j == 0 || all_upper) {
|
|
|
|
|
value[j] = ToUpper(value[j]);
|
|
|
|
|
value[j] = ascii_toupper(value[j]);
|
|
|
|
|
} else {
|
|
|
|
|
// Nothing, already in lower.
|
|
|
|
|
}
|
|
|
|
@ -163,7 +133,7 @@ string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
|
|
|
|
|
result += *i;
|
|
|
|
|
}
|
|
|
|
|
if ((result.length() != 0) && !first_capitalized) {
|
|
|
|
|
result[0] = ToLower(result[0]);
|
|
|
|
|
result[0] = ascii_tolower(result[0]);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
@ -272,7 +242,7 @@ bool IsSpecialName(const string& name, const string* special_names,
|
|
|
|
|
// If name is longer than the retained_name[i] that it matches
|
|
|
|
|
// the next character must be not lower case (newton vs newTon vs
|
|
|
|
|
// new_ton).
|
|
|
|
|
return !IsLower(name[length]);
|
|
|
|
|
return !ascii_islower(name[length]);
|
|
|
|
|
} else {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@ -342,30 +312,6 @@ string FileClassPrefix(const FileDescriptor* file) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ValidateObjCClassPrefix(const FileDescriptor* file) {
|
|
|
|
|
string prefix = file->options().objc_class_prefix();
|
|
|
|
|
if (prefix.length() > 0) {
|
|
|
|
|
// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
|
|
|
|
|
// error cases, so it seems to be ok to use as a back door for errors.
|
|
|
|
|
if (!IsUpper(prefix[0])) {
|
|
|
|
|
cerr << endl
|
|
|
|
|
<< "protoc:0: warning: Invalid 'option objc_class_prefix = \""
|
|
|
|
|
<< prefix << "\";' in '" << file->name() << "';"
|
|
|
|
|
<< " it should start with a capital letter."
|
|
|
|
|
<< endl;
|
|
|
|
|
cerr.flush();
|
|
|
|
|
}
|
|
|
|
|
if (prefix.length() < 3) {
|
|
|
|
|
cerr << endl
|
|
|
|
|
<< "protoc:0: warning: Invalid 'option objc_class_prefix = \""
|
|
|
|
|
<< prefix << "\";' in '" << file->name() << "';"
|
|
|
|
|
<< " Apple recommends they should be at least 3 characters long."
|
|
|
|
|
<< endl;
|
|
|
|
|
cerr.flush();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string FileClassName(const FileDescriptor* file) {
|
|
|
|
|
string name = FileClassPrefix(file);
|
|
|
|
|
name += UnderscoresToCamelCase(StripProto(BaseFileName(file)), true);
|
|
|
|
@ -453,10 +399,10 @@ string UnCamelCaseEnumShortName(const string& name) {
|
|
|
|
|
string result;
|
|
|
|
|
for (int i = 0; i < name.size(); i++) {
|
|
|
|
|
char c = name[i];
|
|
|
|
|
if (i > 0 && c >= 'A' && c <= 'Z') {
|
|
|
|
|
if (i > 0 && ascii_isupper(c)) {
|
|
|
|
|
result += '_';
|
|
|
|
|
}
|
|
|
|
|
result += ToUpper(c);
|
|
|
|
|
result += ascii_toupper(c);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
@ -487,7 +433,7 @@ string FieldNameCapitalized(const FieldDescriptor* field) {
|
|
|
|
|
// name.
|
|
|
|
|
string result = FieldName(field);
|
|
|
|
|
if (result.length() > 0) {
|
|
|
|
|
result[0] = ToUpper(result[0]);
|
|
|
|
|
result[0] = ascii_toupper(result[0]);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
@ -511,7 +457,7 @@ string OneofNameCapitalized(const OneofDescriptor* descriptor) {
|
|
|
|
|
// Use the common handling and then up-case the first letter.
|
|
|
|
|
string result = OneofName(descriptor);
|
|
|
|
|
if (result.length() > 0) {
|
|
|
|
|
result[0] = ToUpper(result[0]);
|
|
|
|
|
result[0] = ascii_toupper(result[0]);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
@ -526,8 +472,8 @@ string UnCamelCaseFieldName(const string& name, const FieldDescriptor* field) {
|
|
|
|
|
}
|
|
|
|
|
if (field->type() == FieldDescriptor::TYPE_GROUP) {
|
|
|
|
|
if (worker.length() > 0) {
|
|
|
|
|
if (worker[0] >= 'a' && worker[0] <= 'z') {
|
|
|
|
|
worker[0] = ToUpper(worker[0]);
|
|
|
|
|
if (ascii_islower(worker[0])) {
|
|
|
|
|
worker[0] = ascii_toupper(worker[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return worker;
|
|
|
|
@ -535,11 +481,11 @@ string UnCamelCaseFieldName(const string& name, const FieldDescriptor* field) {
|
|
|
|
|
string result;
|
|
|
|
|
for (int i = 0; i < worker.size(); i++) {
|
|
|
|
|
char c = worker[i];
|
|
|
|
|
if (c >= 'A' && c <= 'Z') {
|
|
|
|
|
if (ascii_isupper(c)) {
|
|
|
|
|
if (i > 0) {
|
|
|
|
|
result += '_';
|
|
|
|
|
}
|
|
|
|
|
result += ToLower(c);
|
|
|
|
|
result += ascii_tolower(c);
|
|
|
|
|
} else {
|
|
|
|
|
result += c;
|
|
|
|
|
}
|
|
|
|
@ -831,6 +777,252 @@ string BuildCommentsString(const SourceLocation& location) {
|
|
|
|
|
return final_comments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// Internal helper class that parses the expected package to prefix mappings
|
|
|
|
|
// file.
|
|
|
|
|
class Parser {
|
|
|
|
|
public:
|
|
|
|
|
Parser(map<string, string>* inout_package_to_prefix_map)
|
|
|
|
|
: prefix_map_(inout_package_to_prefix_map), line_(0) {}
|
|
|
|
|
|
|
|
|
|
// Parses a check of input, returning success/failure.
|
|
|
|
|
bool ParseChunk(StringPiece chunk);
|
|
|
|
|
|
|
|
|
|
// Should be called to finish parsing (after all input has been provided via
|
|
|
|
|
// ParseChunk()). Returns success/failure.
|
|
|
|
|
bool Finish();
|
|
|
|
|
|
|
|
|
|
int last_line() const { return line_; }
|
|
|
|
|
string error_str() const { return error_str_; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool ParseLoop();
|
|
|
|
|
|
|
|
|
|
map<string, string>* prefix_map_;
|
|
|
|
|
int line_;
|
|
|
|
|
string error_str_;
|
|
|
|
|
StringPiece p_;
|
|
|
|
|
string leftover_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool Parser::ParseChunk(StringPiece chunk) {
|
|
|
|
|
if (!leftover_.empty()) {
|
|
|
|
|
chunk.AppendToString(&leftover_);
|
|
|
|
|
p_ = StringPiece(leftover_);
|
|
|
|
|
} else {
|
|
|
|
|
p_ = chunk;
|
|
|
|
|
}
|
|
|
|
|
bool result = ParseLoop();
|
|
|
|
|
if (p_.empty()) {
|
|
|
|
|
leftover_.clear();
|
|
|
|
|
} else {
|
|
|
|
|
leftover_ = p_.ToString();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Parser::Finish() {
|
|
|
|
|
if (leftover_.empty()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// Force a newline onto the end to finish parsing.
|
|
|
|
|
p_ = StringPiece(leftover_ + "\n");
|
|
|
|
|
if (!ParseLoop()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return p_.empty(); // Everything used?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ascii_isnewline(char c) { return c == '\n' || c == '\r'; }
|
|
|
|
|
|
|
|
|
|
bool ReadLine(StringPiece* input, StringPiece* line) {
|
|
|
|
|
for (int len = 0; len < input->size(); ++len) {
|
|
|
|
|
if (ascii_isnewline((*input)[len])) {
|
|
|
|
|
*line = StringPiece(input->data(), len);
|
|
|
|
|
++len; // advance over the newline
|
|
|
|
|
*input = StringPiece(input->data() + len, input->size() - len);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false; // Ran out of input with no newline.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TrimWhitespace(StringPiece* input) {
|
|
|
|
|
while (!input->empty() && ascii_isspace(*input->data())) {
|
|
|
|
|
input->remove_prefix(1);
|
|
|
|
|
}
|
|
|
|
|
while (!input->empty() && ascii_isspace((*input)[input->length() - 1])) {
|
|
|
|
|
input->remove_suffix(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RemoveComment(StringPiece* input) {
|
|
|
|
|
int offset = input->find('#');
|
|
|
|
|
if (offset != StringPiece::npos) {
|
|
|
|
|
input->remove_suffix(input->length() - offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Parser::ParseLoop() {
|
|
|
|
|
StringPiece line;
|
|
|
|
|
while (ReadLine(&p_, &line)) {
|
|
|
|
|
++line_;
|
|
|
|
|
RemoveComment(&line);
|
|
|
|
|
TrimWhitespace(&line);
|
|
|
|
|
if (line.size() == 0) {
|
|
|
|
|
continue; // Blank line.
|
|
|
|
|
}
|
|
|
|
|
int offset = line.find('=');
|
|
|
|
|
if (offset == StringPiece::npos) {
|
|
|
|
|
error_str_ =
|
|
|
|
|
string("Line without equal sign: '") + line.ToString() + "'.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
StringPiece package(line, 0, offset);
|
|
|
|
|
StringPiece prefix(line, offset + 1, line.length() - offset - 1);
|
|
|
|
|
TrimWhitespace(&package);
|
|
|
|
|
TrimWhitespace(&prefix);
|
|
|
|
|
// Don't really worry about error checking the the package/prefix for
|
|
|
|
|
// being valid. Assume the file is validated when it is created/edited.
|
|
|
|
|
(*prefix_map_)[package.ToString()] = prefix.ToString();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool LoadExpectedPackagePrefixes(map<string, string>* prefix_map,
|
|
|
|
|
string* out_expect_file_path,
|
|
|
|
|
string* out_error) {
|
|
|
|
|
const char* file_path = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
|
|
|
|
|
if (file_path == NULL) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int fd;
|
|
|
|
|
do {
|
|
|
|
|
fd = open(file_path, O_RDONLY);
|
|
|
|
|
} while (fd < 0 && errno == EINTR);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
*out_error =
|
|
|
|
|
string(file_path) + ":0:0: error: Unable to open." + strerror(errno);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
io::FileInputStream file_stream(fd);
|
|
|
|
|
file_stream.SetCloseOnDelete(true);
|
|
|
|
|
*out_expect_file_path = file_path;
|
|
|
|
|
|
|
|
|
|
Parser parser(prefix_map);
|
|
|
|
|
const void* buf;
|
|
|
|
|
int buf_len;
|
|
|
|
|
while (file_stream.Next(&buf, &buf_len)) {
|
|
|
|
|
if (buf_len == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!parser.ParseChunk(StringPiece(static_cast<const char*>(buf), buf_len))) {
|
|
|
|
|
*out_error = string(file_path) + ":" + SimpleItoa(parser.last_line()) +
|
|
|
|
|
":0: error: " + parser.error_str();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return parser.Finish();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
bool ValidateObjCClassPrefix(const FileDescriptor* file, string* out_error) {
|
|
|
|
|
const string prefix = file->options().objc_class_prefix();
|
|
|
|
|
const string package = file->package();
|
|
|
|
|
|
|
|
|
|
// NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some
|
|
|
|
|
// error cases, so it seems to be ok to use as a back door for warnings.
|
|
|
|
|
|
|
|
|
|
// First Check: Warning - if there is a prefix, ensure it is is a reasonable
|
|
|
|
|
// value according to Apple's rules.
|
|
|
|
|
if (prefix.length()) {
|
|
|
|
|
if (!ascii_isupper(prefix[0])) {
|
|
|
|
|
cerr << endl
|
|
|
|
|
<< "protoc:0: warning: Invalid 'option objc_class_prefix = \""
|
|
|
|
|
<< prefix << "\";' in '" << file->name() << "';"
|
|
|
|
|
<< " it should start with a capital letter." << endl;
|
|
|
|
|
cerr.flush();
|
|
|
|
|
}
|
|
|
|
|
if (prefix.length() < 3) {
|
|
|
|
|
cerr << endl
|
|
|
|
|
<< "protoc:0: warning: Invalid 'option objc_class_prefix = \""
|
|
|
|
|
<< prefix << "\";' in '" << file->name() << "';"
|
|
|
|
|
<< " Apple recommends they should be at least 3 characters long."
|
|
|
|
|
<< endl;
|
|
|
|
|
cerr.flush();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load any expected package prefixes to validate against those.
|
|
|
|
|
map<string, string> expected_package_prefixes;
|
|
|
|
|
string expect_file_path;
|
|
|
|
|
if (!LoadExpectedPackagePrefixes(&expected_package_prefixes,
|
|
|
|
|
&expect_file_path, out_error)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If there are no expected prefixes, out of here.
|
|
|
|
|
if (expected_package_prefixes.size() == 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Second Check: Error - See if there was an expected prefix for the package
|
|
|
|
|
// and report if it doesn't match.
|
|
|
|
|
map<string, string>::iterator package_match =
|
|
|
|
|
expected_package_prefixes.find(package);
|
|
|
|
|
if (package_match != expected_package_prefixes.end()) {
|
|
|
|
|
// There was an entry, and...
|
|
|
|
|
if (package_match->second == prefix) {
|
|
|
|
|
// ...it matches. All good, out of here!
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
// ...it didn't match!
|
|
|
|
|
*out_error = "protoc:0: error: Expected 'option objc_class_prefix = \"" +
|
|
|
|
|
package_match->second + "\";' in '" + file->name() + "'";
|
|
|
|
|
if (prefix.length()) {
|
|
|
|
|
*out_error += "; but found '" + prefix + "' instead";
|
|
|
|
|
}
|
|
|
|
|
*out_error += ".";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Third Check: Error - If there was a prefix make sure it wasn't expected
|
|
|
|
|
// for a different package instead (overlap is allowed, but it has to be
|
|
|
|
|
// listed as an expected overlap).
|
|
|
|
|
if (prefix.length()) {
|
|
|
|
|
for (map<string, string>::iterator i = expected_package_prefixes.begin();
|
|
|
|
|
i != expected_package_prefixes.end(); ++i) {
|
|
|
|
|
if (i->second == prefix) {
|
|
|
|
|
*out_error =
|
|
|
|
|
"protoc:0: error: Found 'option objc_class_prefix = \"" + prefix +
|
|
|
|
|
"\";' in '" + file->name() +
|
|
|
|
|
"'; that prefix is already used for 'package " + i->first +
|
|
|
|
|
";'. It can only be reused by listing it in the expected file (" +
|
|
|
|
|
expect_file_path + ").";
|
|
|
|
|
return false; // Only report first usage of the prefix.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fourth Check: Warning - If there was a prefix, and it wasn't expected,
|
|
|
|
|
// issue a warning suggesting it gets added to the file.
|
|
|
|
|
if (prefix.length()) {
|
|
|
|
|
cerr << endl
|
|
|
|
|
<< "protoc:0: warning: Found 'option objc_class_prefix = \"" << prefix
|
|
|
|
|
<< "\";' in '" << file->name() << "';"
|
|
|
|
|
<< " should you add it to the expected prefixes file ("
|
|
|
|
|
<< expect_file_path << ")?" << endl;
|
|
|
|
|
cerr.flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextFormatDecodeData::AddString(int32 key,
|
|
|
|
|
const string& input_for_decode,
|
|
|
|
|
const string& desired_output) {
|
|
|
|
@ -898,7 +1090,7 @@ class DecodeDataBuilder {
|
|
|
|
|
|
|
|
|
|
void AddChar(const char desired) {
|
|
|
|
|
++segment_len_;
|
|
|
|
|
is_all_upper_ &= IsUpper(desired);
|
|
|
|
|
is_all_upper_ &= ascii_isupper(desired);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Push() {
|
|
|
|
@ -913,9 +1105,9 @@ class DecodeDataBuilder {
|
|
|
|
|
bool AddFirst(const char desired, const char input) {
|
|
|
|
|
if (desired == input) {
|
|
|
|
|
op_ = kOpAsIs;
|
|
|
|
|
} else if (desired == ToUpper(input)) {
|
|
|
|
|
} else if (desired == ascii_toupper(input)) {
|
|
|
|
|
op_ = kOpFirstUpper;
|
|
|
|
|
} else if (desired == ToLower(input)) {
|
|
|
|
|
} else if (desired == ascii_tolower(input)) {
|
|
|
|
|
op_ = kOpFirstLower;
|
|
|
|
|
} else {
|
|
|
|
|
// Can't be transformed to match.
|
|
|
|
@ -953,7 +1145,7 @@ bool DecodeDataBuilder::AddCharacter(const char desired, const char input) {
|
|
|
|
|
if (desired == input) {
|
|
|
|
|
// If we aren't transforming it, or we're upper casing it and it is
|
|
|
|
|
// supposed to be uppercase; just add it to the segment.
|
|
|
|
|
if ((op_ != kOpAllUpper) || IsUpper(desired)) {
|
|
|
|
|
if ((op_ != kOpAllUpper) || ascii_isupper(desired)) {
|
|
|
|
|
AddChar(desired);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@ -965,7 +1157,7 @@ bool DecodeDataBuilder::AddCharacter(const char desired, const char input) {
|
|
|
|
|
|
|
|
|
|
// If we need to uppercase, and everything so far has been uppercase,
|
|
|
|
|
// promote op to AllUpper.
|
|
|
|
|
if ((desired == ToUpper(input)) && is_all_upper_) {
|
|
|
|
|
if ((desired == ascii_toupper(input)) && is_all_upper_) {
|
|
|
|
|
op_ = kOpAllUpper;
|
|
|
|
|
AddChar(desired);
|
|
|
|
|
return true;
|
|
|
|
|