Merge pull request #500 from silvergasp/master

Add bazel rules for nanopb generation
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b7f43c9..bf42cd0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -123,7 +123,7 @@
             ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
         target_include_directories(protobuf-nanopb-static INTERFACE
             $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
-	        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
         )
     endif()
 
diff --git a/docs/reference.md b/docs/reference.md
index 2d591ae..7f0d85d 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -2,12 +2,13 @@
 
 ## Compilation options
 
-The following options can be specified in one of two ways:
+Compilation options affect the functionality included in the nanopb core C code.
+The options can be specified in one of two ways:
 
 1.  Using the -D switch on the C compiler command line.
 2.  Using a `#define` at the top of pb.h.
 
-> **NOTE:** You must have the same settings for the nanopb library and all code that
+> **NOTE:** You must have the same compilation options for the nanopb library and all code that
 includes nanopb headers.
 
 * `PB_ENABLE_MALLOC`: Enable dynamic allocation support in the decoder.
@@ -29,10 +30,12 @@
 generated `.pb.c` files. The default setting is to use the smallest
 datatypes (least resources used).
 
-## Proto file options
+## Generator options
 
-The generator behaviour can be adjusted using several options, defined
-in the [nanopb.proto](https://github.com/nanopb/nanopb/blob/master/generator/proto/nanopb.proto) file in the generator folder. Here is a list of the most common options, but see the file for a full list:
+Generator options affect how the `.proto` files get converted to `.pb.c` and `.pb.h.` files.
+
+Most options are related to specific message or field in `.proto` file.
+The full set of available options is defined in [nanopb.proto](https://github.com/nanopb/nanopb/blob/master/generator/proto/nanopb.proto). Here is a list of the most common options, but see the file for a full list:
 
 * `max_size`: Allocated maximum size for `bytes` and `string` fields. For strings, this includes the terminating zero.
 * `max_length`: Maximum length for `string` fields. Setting this is equivalent to setting `max_size` to a value of length + 1.
@@ -50,7 +53,7 @@
 * `int_size`: Override the integer type of a field. For example, specify `int_size = IS_8` to convert `int32` from protocol definition into `int8_t` in the structure.
 
 These options can be defined for the .proto files before they are
-converted using the nanopb-generatory.py. There are three ways to define
+converted using the nanopb-generator.py. There are three ways to define
 the options:
 
 1.  Using a separate .options file. This allows using wildcards for
@@ -122,11 +125,6 @@
 If preferred, the name of the options file can be set using generator
 argument `-f`.
 
-### Defining the options on command line
-
-The nanopb_generator.py has a simple command line option `-s OPTION:VALUE`.
-The setting applies to the whole file that is being processed.
-
 ### Defining the options in the .proto file
 
 The .proto file format allows defining custom options for the fields.
@@ -159,6 +157,19 @@
 }
 ~~~~
 
+### Defining the options on command line
+
+The nanopb_generator.py has a simple command line option `-s OPTION:VALUE`.
+The setting applies to the whole file that is being processed.
+
+There are also a few command line options that cannot be applied using the
+other mechanisms, as they affect the whole generation:
+
+* `--c-style`: Modify symbol names to better match C naming conventions.
+* `--no-timestamp`: Do not add timestamp to generated files.
+* `--strip-path`: Remove relative path from generated `#include` directives.
+* `--cpp-descriptors`: Generate extra convenience definitions for use from C++
+
 ## pb.h
 
 ### pb_byte_t
diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index b7cddde..0bec974 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -143,12 +143,70 @@
     (FieldD.TYPE_UINT64, nanopb_pb2.IS_64):   ('uint64_t','UINT64', 10,  8),
 }
 
+class NamingStyle:
+    def enum_name(self, name):
+        return "_%s" % (name)
+
+    def struct_name(self, name):
+        return "_%s" % (name)
+
+    def type_name(self, name):
+        return "%s" % (name)
+
+    def define_name(self, name):
+        return "%s" % (name)
+
+    def var_name(self, name):
+        return "%s" % (name)
+
+    def enum_entry(self, name):
+        return "%s" % (name)
+
+    def func_name(self, name):
+        return "%s" % (name)
+
+    def bytes_type(self, struct_name, name):
+        return "%s_%s_t" % (struct_name, name)
+
+class NamingStyleC(NamingStyle):
+    def enum_name(self, name):
+        return self.underscore(name)
+
+    def struct_name(self, name):
+        return self.underscore(name)
+
+    def type_name(self, name):
+        return "%s_t" % self.underscore(name)
+
+    def define_name(self, name):
+        return self.underscore(name).upper()
+
+    def var_name(self, name):
+        return self.underscore(name)
+
+    def enum_entry(self, name):
+        return self.underscore(name).upper()
+
+    def func_name(self, name):
+        return self.underscore(name)
+
+    def bytes_type(self, struct_name, name):
+        return "%s_%s_t" % (self.underscore(struct_name), self.underscore(name))
+
+    def underscore(self, word):
+        word = str(word)
+        word = re.sub(r"([A-Z]+)([A-Z][a-z])", r'\1_\2', word)
+        word = re.sub(r"([a-z\d])([A-Z])", r'\1_\2', word)
+        word = word.replace("-", "_")
+        return word.lower()
+
 class Globals:
     '''Ugly global variables, should find a good way to pass these.'''
     verbose_options = False
     separate_options = []
     matched_namemasks = set()
     protoc_insertion_points = False
+    naming_style = NamingStyle()
 
 # String types and file encoding for Python2 UTF-8 support
 if sys.version_info.major == 2:
@@ -286,17 +344,17 @@
     FIELD = 2
     MESSAGE = 4
     ENUM = 5
+    NESTED_TYPE = 3
+    NESTED_ENUM = 4
 
-    def __init__(self, element_type, index, comments = None, parent = ()):
+    def __init__(self, path, comments = None):
         '''
-        element_type is a predefined value for each element type in proto file.
-            For example, message == 4, enum == 5, service == 6
-        index is the N-th occurrence of the `path` in the proto file.
-            For example, 4-th message in the proto file or 2-nd enum etc ...
+        path is a tuple containing integers (type, index, ...)
         comments is a dictionary mapping between element path & SourceCodeInfo.Location
             (contains information about source comments).
         '''
-        self.element_path = parent + (element_type, index)
+        assert(isinstance(path, tuple))
+        self.element_path = path
         self.comments = comments or {}
 
     def get_member_comments(self, index):
@@ -332,14 +390,14 @@
 
 
 class Enum(ProtoElement):
-    def __init__(self, names, desc, enum_options, index, comments):
+    def __init__(self, names, desc, enum_options, element_path, comments):
         '''
         desc is EnumDescriptorProto
         index is the index of this enum element inside the file
         comments is a dictionary mapping between element path & SourceCodeInfo.Location
             (contains information about source comments)
         '''
-        super(Enum, self).__init__(ProtoElement.ENUM, index, comments)
+        super(Enum, self).__init__(element_path, comments)
 
         self.options = enum_options
         self.names = names
@@ -371,7 +429,11 @@
         if leading_comment:
             result = '%s\n' % leading_comment
 
-        result += 'typedef enum _%s { %s\n' % (self.names, trailing_comment)
+        result += 'typedef enum %s {' % Globals.naming_style.enum_name(self.names)
+        if trailing_comment:
+            result += " " + trailing_comment
+
+        result += "\n"
 
         enum_length = len(self.values)
         enum_values = []
@@ -386,7 +448,11 @@
                 # last enum member should not end with a comma
                 comma = ""
 
-            enum_values.append("    %s = %d%s %s" % (name, value, comma, trailing_comment))
+            enum_value = "    %s = %d%s" % (Globals.naming_style.enum_entry(name), value, comma)
+            if trailing_comment:
+                enum_value += " " + trailing_comment
+
+            enum_values.append(enum_value)
 
         result += '\n'.join(enum_values)
         result += '\n}'
@@ -394,15 +460,22 @@
         if self.packed:
             result += ' pb_packed'
 
-        result += ' %s;' % self.names
+        result += ' %s;' % Globals.naming_style.type_name(self.names)
         return result
 
     def auxiliary_defines(self):
         # sort the enum by value
         sorted_values = sorted(self.values, key = lambda x: (x[1], x[0]))
-        result  = '#define _%s_MIN %s\n' % (self.names, sorted_values[0][0])
-        result += '#define _%s_MAX %s\n' % (self.names, sorted_values[-1][0])
-        result += '#define _%s_ARRAYSIZE ((%s)(%s+1))\n' % (self.names, self.names, sorted_values[-1][0])
+        result  = '#define %s %s\n' % (
+            Globals.naming_style.define_name('_%s_MIN' % self.names),
+            Globals.naming_style.enum_entry(sorted_values[0][0]))
+        result += '#define %s %s\n' % (
+            Globals.naming_style.define_name('_%s_MAX' % self.names),
+            Globals.naming_style.enum_entry(sorted_values[-1][0]))
+        result += '#define %s ((%s)(%s+1))\n' % (
+            Globals.naming_style.define_name('_%s_ARRAYSIZE' % self.names),
+            Globals.naming_style.type_name(self.names),
+            Globals.naming_style.enum_entry(sorted_values[-1][0]))
 
         if not self.options.long_names:
             # Define the long names always so that enum value references
@@ -411,7 +484,9 @@
                 result += '#define %s %s\n' % (self.value_longnames[i], x[0])
 
         if self.options.enum_to_string:
-            result += 'const char *%s_name(%s v);\n' % (self.names, self.names)
+            result += 'const char *%s(%s v);\n' % (
+                Globals.naming_style.func_name('%s_name' % self.names),
+                Globals.naming_style.type_name(self.names))
 
         return result
 
@@ -419,13 +494,18 @@
         if not self.options.enum_to_string:
             return ""
 
-        result = 'const char *%s_name(%s v) {\n' % (self.names, self.names)
+        result = 'const char *%s(%s v) {\n' % (
+            Globals.naming_style.func_name('%s_name' % self.names),
+            Globals.naming_style.type_name(self.names))
+
         result += '    switch (v) {\n'
 
         for ((enumname, _), strname) in zip(self.values, self.value_longnames):
             # Strip off the leading type name from the string value.
             strval = str(strname)[len(str(self.names)) + 1:]
-            result += '        case %s: return "%s";\n' % (enumname, strval)
+            result += '        case %s: return "%s";\n' % (
+                Globals.naming_style.enum_entry(enumname),
+                Globals.naming_style.enum_entry(strval))
 
         result += '    }\n'
         result += '    return "unknown";\n'
@@ -455,9 +535,9 @@
     macro_x_param = 'X'
     macro_a_param = 'a'
 
-    def __init__(self, struct_name, desc, field_options, parent_path = (), index = None, comments = None):
+    def __init__(self, struct_name, desc, field_options, element_path = (), comments = None):
         '''desc is FieldDescriptorProto'''
-        ProtoElement.__init__(self, ProtoElement.FIELD, index, comments, parent_path)
+        ProtoElement.__init__(self, element_path, comments)
         self.tag = desc.number
         self.struct_name = struct_name
         self.union_name = None
@@ -470,7 +550,6 @@
         self.data_item_size = None
         self.ctype = None
         self.fixed_count = False
-        self.index = index
         self.callback_datatype = field_options.callback_datatype
         self.math_include_required = False
         self.sort_by_tag = field_options.sort_by_tag
@@ -600,7 +679,7 @@
                 self.pbtype = 'BYTES'
                 self.ctype = 'pb_bytes_array_t'
                 if self.allocation == 'STATIC':
-                    self.ctype = self.struct_name + self.name + 't'
+                    self.ctype = Globals.naming_style.bytes_type(self.struct_name, self.name)
                     self.enc_size = varint_max_size(self.max_size) + self.max_size
         elif desc.type == FieldD.TYPE_MESSAGE:
             self.pbtype = 'MESSAGE'
@@ -620,34 +699,39 @@
 
     def __str__(self):
         result = ''
+
+        var_name = Globals.naming_style.var_name(self.name)
+        type_name = Globals.naming_style.type_name(self.ctype) if isinstance(self.ctype, Names) else self.ctype
+
         if self.allocation == 'POINTER':
             if self.rules == 'REPEATED':
                 if self.pbtype == 'MSG_W_CB':
-                    result += '    pb_callback_t cb_' + self.name + ';\n'
-                result += '    pb_size_t ' + self.name + '_count;\n'
+                    result += '    pb_callback_t cb_' + var_name + ';\n'
+                result += '    pb_size_t ' + var_name + '_count;\n'
 
             if self.pbtype in ['MESSAGE', 'MSG_W_CB']:
                 # Use struct definition, so recursive submessages are possible
-                result += '    struct _%s *%s;' % (self.ctype, self.name)
+                result += '    struct %s *%s;' % (Globals.naming_style.struct_name(self.ctype), var_name)
             elif self.pbtype == 'FIXED_LENGTH_BYTES' or self.rules == 'FIXARRAY':
                 # Pointer to fixed size array
-                result += '    %s (*%s)%s;' % (self.ctype, self.name, self.array_decl)
+                result += '    %s (*%s)%s;' % (type_name, var_name, self.array_decl)
             elif self.rules in ['REPEATED', 'FIXARRAY'] and self.pbtype in ['STRING', 'BYTES']:
                 # String/bytes arrays need to be defined as pointers to pointers
-                result += '    %s **%s;' % (self.ctype, self.name)
+                result += '    %s **%s;' % (type_name, var_name)
             else:
-                result += '    %s *%s;' % (self.ctype, self.name)
+                result += '    %s *%s;' % (type_name, var_name)
         elif self.allocation == 'CALLBACK':
-            result += '    %s %s;' % (self.callback_datatype, self.name)
+            result += '    %s %s;' % (self.callback_datatype, var_name)
         else:
             if self.pbtype == 'MSG_W_CB' and self.rules in ['OPTIONAL', 'REPEATED']:
-                result += '    pb_callback_t cb_' + self.name + ';\n'
+                result += '    pb_callback_t cb_' + var_name + ';\n'
 
             if self.rules == 'OPTIONAL':
-                result += '    bool has_' + self.name + ';\n'
+                result += '    bool has_' + var_name + ';\n'
             elif self.rules == 'REPEATED':
-                result += '    pb_size_t ' + self.name + '_count;\n'
-            result += '    %s %s%s;' % (self.ctype, self.name, self.array_decl)
+                result += '    pb_size_t ' + var_name + '_count;\n'
+
+            result += '    %s %s%s;' % (type_name, var_name, self.array_decl)
 
         leading_comment, trailing_comment = self.get_comments(leading_indent = True)
         if leading_comment: result = leading_comment + "\n" + result
@@ -658,7 +742,7 @@
     def types(self):
         '''Return definitions for any special types this field might need.'''
         if self.pbtype == 'BYTES' and self.allocation == 'STATIC':
-            result = 'typedef PB_BYTES_ARRAY_T(%d) %s;\n' % (self.max_size, self.ctype)
+            result = 'typedef PB_BYTES_ARRAY_T(%d) %s;\n' % (self.max_size, Globals.naming_style.var_name(self.ctype))
         else:
             result = ''
         return result
@@ -679,9 +763,9 @@
         inner_init = None
         if self.pbtype in ['MESSAGE', 'MSG_W_CB']:
             if null_init:
-                inner_init = '%s_init_zero' % self.ctype
+                inner_init = Globals.naming_style.define_name('%s_init_zero' % self.ctype)
             else:
-                inner_init = '%s_init_default' % self.ctype
+                inner_init =  Globals.naming_style.define_name('%s_init_default' % self.ctype)
         elif self.default is None or null_init:
             if self.pbtype == 'STRING':
                 inner_init = '""'
@@ -690,7 +774,7 @@
             elif self.pbtype == 'FIXED_LENGTH_BYTES':
                 inner_init = '{0}'
             elif self.pbtype in ('ENUM', 'UENUM'):
-                inner_init = '_%s_MIN' % self.ctype
+                inner_init = '_%s_MIN' % Globals.naming_style.define_name(self.ctype)
             else:
                 inner_init = '0'
         else:
@@ -764,22 +848,29 @@
 
     def tags(self):
         '''Return the #define for the tag number of this field.'''
-        identifier = '%s_%s_tag' % (self.struct_name, self.name)
+        identifier = Globals.naming_style.define_name('%s_%s_tag' % (self.struct_name, self.name))
         return '#define %-40s %d\n' % (identifier, self.tag)
 
     def fieldlist(self):
         '''Return the FIELDLIST macro entry for this field.
         Format is: X(a, ATYPE, HTYPE, LTYPE, field_name, tag)
         '''
-        name = self.name
+        name = Globals.naming_style.var_name(self.name)
 
         if self.rules == "ONEOF":
           # For oneofs, make a tuple of the union name, union member name,
           # and the name inside the parent struct.
           if not self.anonymous:
-            name = '(%s,%s,%s)' % (self.union_name, self.name, self.union_name + '.' + self.name)
+            name = '(%s,%s,%s)' % (
+                Globals.naming_style.var_name(self.union_name),
+                Globals.naming_style.var_name(self.name),
+                Globals.naming_style.var_name(self.union_name) + '.' +
+                Globals.naming_style.var_name(self.name))
           else:
-            name = '(%s,%s,%s)' % (self.union_name, self.name, self.name)
+            name = '(%s,%s,%s)' % (
+                Globals.naming_style.var_name(self.union_name),
+                Globals.naming_style.var_name(self.name),
+                Globals.naming_style.var_name(self.name))
 
         return '%s(%s, %-9s %-9s %-9s %-16s %3d)' % (self.macro_x_param,
                                                      self.macro_a_param,
@@ -968,13 +1059,13 @@
         else:
             self.skip = False
             self.rules = 'REQUIRED' # We don't really want the has_field for extensions
-            # currently no support for comments for extension fields => provide 0, {}
-            self.msg = Message(self.fullname + "extmsg", None, field_options, 0, {})
+            # currently no support for comments for extension fields => provide (), {}
+            self.msg = Message(self.fullname + "extmsg", None, field_options, (), {})
             self.msg.fields.append(self)
 
     def tags(self):
         '''Return the #define for the tag number of this field.'''
-        identifier = '%s_tag' % self.fullname
+        identifier = Globals.naming_style.define_name('%s_tag' % (self.fullname))
         return '#define %-40s %d\n' % (identifier, self.tag)
 
     def extension_decl(self):
@@ -985,7 +1076,7 @@
             return msg
 
         return ('extern const pb_extension_type_t %s; /* field type: %s */\n' %
-            (self.fullname, str(self).strip()))
+            (Globals.naming_style.var_name(self.fullname), str(self).strip()))
 
     def extension_def(self, dependencies):
         '''Definition of the extension type in the .pb.c file'''
@@ -998,10 +1089,10 @@
         result += self.msg.fields_declaration(dependencies)
         result += 'pb_byte_t %s_default[] = {0x00};\n' % self.msg.name
         result += self.msg.fields_definition(dependencies)
-        result += 'const pb_extension_type_t %s = {\n' % self.fullname
+        result += 'const pb_extension_type_t %s = {\n' % Globals.naming_style.var_name(self.fullname)
         result += '    NULL,\n'
         result += '    NULL,\n'
-        result += '    &%s_msg\n' % self.msg.name
+        result += '    &%s_msg\n' % Globals.naming_style.type_name(self.msg.name)
         result += '};\n'
         return result
 
@@ -1020,7 +1111,6 @@
         self.allocation = 'ONEOF'
         self.default = None
         self.rules = 'ONEOF'
-        self.index = None
         self.anonymous = oneof_options.anonymous_oneof
         self.sort_by_tag = oneof_options.sort_by_tag
         self.has_msg_cb = False
@@ -1044,16 +1134,16 @@
         result = ''
         if self.fields:
             if self.has_msg_cb:
-                result += '    pb_callback_t cb_' + self.name + ';\n'
+                result += '    pb_callback_t cb_' + Globals.naming_style.var_name(self.name) + ';\n'
 
-            result += '    pb_size_t which_' + self.name + ";\n"
+            result += '    pb_size_t which_' + Globals.naming_style.var_name(self.name) + ";\n"
             result += '    union {\n'
             for f in self.fields:
                 result += '    ' + str(f).replace('\n', '\n    ') + '\n'
             if self.anonymous:
                 result += '    };'
             else:
-                result += '    } ' + self.name + ';'
+                result += '    } ' + Globals.naming_style.var_name(self.name) + ';'
         return result
 
     def types(self):
@@ -1123,8 +1213,8 @@
 
 
 class Message(ProtoElement):
-    def __init__(self, names, desc, message_options, index, comments):
-        super(Message, self).__init__(ProtoElement.MESSAGE, index, comments)
+    def __init__(self, names, desc, message_options, element_path, comments):
+        super(Message, self).__init__(element_path, comments)
         self.name = names
         self.fields = []
         self.oneofs = {}
@@ -1174,7 +1264,7 @@
             if field_options.descriptorsize > self.descriptorsize:
                 self.descriptorsize = field_options.descriptorsize
 
-            field = Field(self.name, f, field_options, self.element_path, index, self.comments)
+            field = Field(self.name, f, field_options, self.element_path + (ProtoElement.FIELD, index), self.comments)
             if hasattr(f, 'oneof_index') and f.HasField('oneof_index'):
                 if hasattr(f, 'proto3_optional') and f.proto3_optional:
                     no_unions.append(f.oneof_index)
@@ -1215,7 +1305,11 @@
         if leading_comment:
             result = '%s\n' % leading_comment
 
-        result += 'typedef struct _%s { %s\n' % (self.name, trailing_comment)
+        result += 'typedef struct %s {' % Globals.naming_style.struct_name(self.name)
+        if trailing_comment:
+            result += " " + trailing_comment
+
+        result += '\n'
 
         if not self.fields:
             # Empty structs are not allowed in C standard.
@@ -1232,7 +1326,7 @@
         if self.packed:
             result += ' pb_packed'
 
-        result += ' %s;' % self.name
+        result += ' %s;' % Globals.naming_style.type_name(self.name)
 
         if self.packed:
             result = 'PB_PACKED_STRUCT_START\n' + result
@@ -1301,9 +1395,10 @@
         sorted_fields = list(self.all_fields())
         sorted_fields.sort(key = lambda x: x.tag)
 
-        result = '#define %s_FIELDLIST(%s, %s) \\\n' % (self.name,
-                                                        Field.macro_x_param,
-                                                        Field.macro_a_param)
+        result = '#define %s_FIELDLIST(%s, %s) \\\n' % (
+            Globals.naming_style.define_name(self.name),
+            Field.macro_x_param,
+            Field.macro_a_param)
         result += ' \\\n'.join(x.fieldlist() for x in sorted_fields)
         result += '\n'
 
@@ -1311,23 +1406,36 @@
         if has_callbacks:
             if self.callback_function != 'pb_default_field_callback':
                 result += "extern bool %s(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field);\n" % self.callback_function
-            result += "#define %s_CALLBACK %s\n" % (self.name, self.callback_function)
+            result += "#define %s_CALLBACK %s\n" % (
+                Globals.naming_style.define_name(self.name),
+                self.callback_function)
         else:
-            result += "#define %s_CALLBACK NULL\n" % self.name
+            result += "#define %s_CALLBACK NULL\n" % Globals.naming_style.define_name(self.name)
 
         defval = self.default_value(dependencies)
         if defval:
             hexcoded = ''.join("\\x%02x" % ord(defval[i:i+1]) for i in range(len(defval)))
-            result += '#define %s_DEFAULT (const pb_byte_t*)"%s\\x00"\n' % (self.name, hexcoded)
+            result += '#define %s_DEFAULT (const pb_byte_t*)"%s\\x00"\n' % (
+                Globals.naming_style.define_name(self.name),
+                hexcoded)
         else:
-            result += '#define %s_DEFAULT NULL\n' % self.name
+            result += '#define %s_DEFAULT NULL\n' % Globals.naming_style.define_name(self.name)
 
         for field in sorted_fields:
             if field.pbtype in ['MESSAGE', 'MSG_W_CB']:
                 if field.rules == 'ONEOF':
-                    result += "#define %s_%s_%s_MSGTYPE %s\n" % (self.name, field.union_name, field.name, field.ctype)
+                    result += "#define %s_%s_%s_MSGTYPE %s\n" % (
+                        Globals.naming_style.type_name(self.name),
+                        Globals.naming_style.var_name(field.union_name),
+                        Globals.naming_style.var_name(field.name),
+                        Globals.naming_style.type_name(field.ctype)
+                    )
                 else:
-                    result += "#define %s_%s_MSGTYPE %s\n" % (self.name, field.name, field.ctype)
+                    result += "#define %s_%s_MSGTYPE %s\n" % (
+                        Globals.naming_style.type_name(self.name),
+                        Globals.naming_style.var_name(field.name),
+                        Globals.naming_style.type_name(field.ctype)
+                    )
 
         return result
 
@@ -1347,7 +1455,10 @@
         if width == 1:
           width = 'AUTO'
 
-        result = 'PB_BIND(%s, %s, %s)\n' % (self.name, self.name, width)
+        result = 'PB_BIND(%s, %s, %s)\n' % (
+            Globals.naming_style.define_name(self.name),
+            Globals.naming_style.type_name(self.name),
+            width)
         return result
 
     def required_descriptor_width(self, dependencies):
@@ -1476,21 +1587,24 @@
 #                    Processing of entire .proto files
 # ---------------------------------------------------------------------------
 
-def iterate_messages(desc, flatten = False, names = Names()):
-    '''Recursively find all messages. For each, yield name, DescriptorProto.'''
+def iterate_messages(desc, flatten = False, names = Names(), comment_path = ()):
+    '''Recursively find all messages. For each, yield name, DescriptorProto, comment_path.'''
     if hasattr(desc, 'message_type'):
         submsgs = desc.message_type
+        comment_path += (ProtoElement.MESSAGE,)
     else:
         submsgs = desc.nested_type
+        comment_path += (ProtoElement.NESTED_TYPE,)
 
-    for submsg in submsgs:
+    for idx, submsg in enumerate(submsgs):
         sub_names = names + submsg.name
+        sub_path = comment_path + (idx,)
         if flatten:
-            yield Names(submsg.name), submsg
+            yield Names(submsg.name), submsg, sub_path
         else:
-            yield sub_names, submsg
+            yield sub_names, submsg, sub_path
 
-        for x in iterate_messages(submsg, flatten, sub_names):
+        for x in iterate_messages(submsg, flatten, sub_names, sub_path):
             yield x
 
 def iterate_extensions(desc, flatten = False, names = Names()):
@@ -1500,7 +1614,7 @@
     for extension in desc.extension:
         yield names, extension
 
-    for subname, subdesc in iterate_messages(desc, flatten, names):
+    for subname, subdesc, comment_path in iterate_messages(desc, flatten, names):
         for extension in subdesc.extension:
             yield subname, extension
 
@@ -1654,9 +1768,10 @@
         for index, enum in enumerate(self.fdesc.enum_type):
             name = self.manglenames.create_name(enum.name)
             enum_options = get_nanopb_suboptions(enum, self.file_options, name)
-            self.enums.append(Enum(name, enum, enum_options, index, self.comment_locations))
+            enum_path = (ProtoElement.ENUM, index)
+            self.enums.append(Enum(name, enum, enum_options, enum_path, self.comment_locations))
 
-        for index, (names, message) in enumerate(iterate_messages(self.fdesc, self.manglenames.flatten)):
+        for names, message, comment_path in iterate_messages(self.fdesc, self.manglenames.flatten):
             name = self.manglenames.create_name(names)
             message_options = get_nanopb_suboptions(message, self.file_options, name)
 
@@ -1668,11 +1783,12 @@
                 if field.type in (FieldD.TYPE_MESSAGE, FieldD.TYPE_ENUM):
                     field.type_name = self.manglenames.mangle_field_typename(field.type_name)
 
-            self.messages.append(Message(name, message, message_options, index, self.comment_locations))
+            self.messages.append(Message(name, message, message_options, comment_path, self.comment_locations))
             for index, enum in enumerate(message.enum_type):
                 name = self.manglenames.create_name(names + enum.name)
                 enum_options = get_nanopb_suboptions(enum, message_options, name)
-                self.enums.append(Enum(name, enum, enum_options, index, self.comment_locations))
+                enum_path = comment_path + (ProtoElement.NESTED_ENUM, index)
+                self.enums.append(Enum(name, enum, enum_options, enum_path, self.comment_locations))
 
         for names, extension in iterate_extensions(self.fdesc, self.manglenames.flatten):
             name = self.manglenames.create_name(names + extension.name)
@@ -1791,10 +1907,10 @@
         if self.messages:
             yield '/* Initializer values for message structs */\n'
             for msg in self.messages:
-                identifier = '%s_init_default' % msg.name
+                identifier = Globals.naming_style.define_name('%s_init_default' % msg.name)
                 yield '#define %-40s %s\n' % (identifier, msg.get_initializer(False))
             for msg in self.messages:
-                identifier = '%s_init_zero' % msg.name
+                identifier = Globals.naming_style.define_name('%s_init_zero' % msg.name)
                 yield '#define %-40s %s\n' % (identifier, msg.get_initializer(True))
             yield '\n'
 
@@ -1810,12 +1926,14 @@
             for msg in self.messages:
                 yield msg.fields_declaration(self.dependencies) + '\n'
             for msg in self.messages:
-                yield 'extern const pb_msgdesc_t %s_msg;\n' % msg.name
+                yield 'extern const pb_msgdesc_t %s_msg;\n' % Globals.naming_style.type_name(msg.name)
             yield '\n'
 
             yield '/* Defines for backwards compatibility with code written before nanopb-0.4.0 */\n'
             for msg in self.messages:
-              yield '#define %s_fields &%s_msg\n' % (msg.name, msg.name)
+              yield '#define %s &%s_msg\n' % (
+                Globals.naming_style.define_name('%s_fields' % msg.name),
+                Globals.naming_style.type_name(msg.name))
             yield '\n'
 
             yield '/* Maximum encoded size of messages (where known) */\n'
@@ -1850,7 +1968,8 @@
                     cpp_guard = msize.get_cpp_guard(local_defines)
                     if cpp_guard not in guards:
                         guards[cpp_guard] = set()
-                    guards[cpp_guard].add('#define %-40s %s' % (identifier, msize))
+                    guards[cpp_guard].add('#define %-40s %s' % (
+                        Globals.naming_style.define_name(identifier), msize))
                 else:
                     yield '/* %s depends on runtime parameters */\n' % identifier
             for guard, values in guards.items():
@@ -2106,7 +2225,9 @@
 optparser.add_option("-s", dest="settings", metavar="OPTION:VALUE", action="append", default=[],
     help="Set generator option (max_size, max_count etc.).")
 optparser.add_option("--protoc-insertion-points", dest="protoc_insertion_points", action="store_true", default=False,
-                     help="Include insertion point comments in output for use by custom protoc plugins")
+    help="Include insertion point comments in output for use by custom protoc plugins")
+optparser.add_option("-C", "--c-style", dest="c_style", action="store_true", default=False,
+    help="Use C naming convention.")
 
 def parse_file(filename, fdesc, options):
     '''Parse a single file. Returns a ProtoFile instance.'''
@@ -2238,6 +2359,9 @@
         sys.stderr.write('Google Python protobuf library imported from %s, version %s\n'
                          % (google.protobuf.__file__, google.protobuf.__version__))
 
+    if options.c_style:
+        Globals.naming_style = NamingStyleC()
+
     # Load .pb files into memory and compile any .proto files.
     include_path = ['-I%s' % p for p in options.options_path]
     all_fdescs = {}
@@ -2339,6 +2463,9 @@
 
     Globals.verbose_options = options.verbose
 
+    if options.c_style:
+        Globals.naming_style = NamingStyleC()
+
     if options.verbose:
         sys.stderr.write("Nanopb version %s\n" % nanopb_version)
         sys.stderr.write('Google Python protobuf library imported from %s, version %s\n'
diff --git a/tests/comments/comments.expected b/tests/comments/comments.expected
index 0d96dc2..ae22a92 100644
--- a/tests/comments/comments.expected
+++ b/tests/comments/comments.expected
@@ -8,3 +8,7 @@
 m2member4.*m2comment4
 m2oneof10.*m2oneof10_comment
 m2oneof5.*m2oneof5_comment
+A.*A_comment
+B.*B_comment
+C.*C_comment
+subfield.*subfield_comment
diff --git a/tests/comments/comments.proto b/tests/comments/comments.proto
index 0dba90f..0f952ce 100644
--- a/tests/comments/comments.proto
+++ b/tests/comments/comments.proto
@@ -27,3 +27,17 @@
         int32 m2oneof5 = 5; // m2oneof5_comment
     }
 }
+
+message Message3
+{
+    message SubMessage {
+        required int32 subfield = 1; // subfield_comment
+
+        enum SubEnum
+        {
+            A = 0; /// A_comment
+            B = 1; /// B_comment
+            C = 2; /// C_comment
+        }
+    }
+}
diff --git a/tests/namingstyle/SConscript b/tests/namingstyle/SConscript
new file mode 100644
index 0000000..3922340
--- /dev/null
+++ b/tests/namingstyle/SConscript
@@ -0,0 +1,11 @@
+# Test namingstyle option
+
+Import('env')
+
+env = env.Clone()
+env.Replace(NANOPBFLAGS = "-C")
+
+env.NanopbProto(["naming_style", "naming_style.options"])
+
+test = env.Program(["test_naming_style_c.c", "naming_style.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_encode.o", '$COMMON/pb_common.o'])
+env.RunTest(test)
diff --git a/tests/namingstyle/naming_style.options b/tests/namingstyle/naming_style.options
new file mode 100644
index 0000000..0026b0b
--- /dev/null
+++ b/tests/namingstyle/naming_style.options
@@ -0,0 +1,15 @@
+* long_names:true
+* enum_to_string:true
+
+MainMessage.repeatedNumber  max_count:4, fixed_count:true
+MainMessage.string_Values1  type:FT_POINTER
+MainMessage.stringValues2   max_length:40, max_count:5
+MainMessage.requiredString  max_length:10
+MainMessage.repeatedFixed32 max_count:10
+MainMessage.requiredBytes1  max_size:10, fixed_length:true
+MainMessage.requiredBytes2  max_size:10
+MainMessage.repeatedBytes1  type:FT_POINTER
+MainMessage.repeatedBytes2  type:FT_POINTER, fixed_count:true, max_count:5
+MainMessage.repeatedInts    type:FT_POINTER
+MainMessage.SUB_MESSAGE2    type:FT_CALLBACK
+MainMessage.oneOfName2      anonymous_oneof:true
diff --git a/tests/namingstyle/naming_style.proto b/tests/namingstyle/naming_style.proto
new file mode 100644
index 0000000..7e626e8
--- /dev/null
+++ b/tests/namingstyle/naming_style.proto
@@ -0,0 +1,62 @@
+syntax = "proto2";
+
+enum MyEnum1 {
+  ENTRY_FIRST   = 0;
+  ENTRY_Second  = 1;
+  EnumThird     = 2;
+}
+
+enum MY_ENUM2 {
+  ENUM2_ENTRY = 0;
+}
+
+message SubMessage {
+  optional int32 test_value = 1;
+}
+
+message MainMessage {
+  optional int32      LUCKY_number    = 1;
+  required int32      REQUIRED_NUMBER = 2;
+  repeated int32      repeatedNumber  = 3;
+  repeated int32      repeatedInts    = 4;
+
+  optional MyEnum1    MyEnum1         = 5;
+  optional MY_ENUM2   My_Enum2        = 6;
+  required MY_ENUM2   MY_ENUM3        = 7;
+  repeated MY_ENUM2   MY_ENUM4        = 8;
+
+  repeated string     string_Values1  = 9;
+  repeated string     stringValues2   = 10;
+  optional string     OPTIONAL_String = 11;
+  required string     requiredString  = 12;
+
+  repeated fixed32    repeatedFixed32 = 13;
+
+  required bytes      requiredBytes1  = 14;
+  required bytes      requiredBytes2  = 15;
+  repeated bytes      repeatedBytes1  = 16;
+  repeated bytes      repeatedBytes2  = 17;
+
+  optional SubMessage subMessage1     = 18;
+  repeated SubMessage SUB_MESSAGE2    = 19;
+  required SubMessage sub_message3    = 20;
+
+  oneof oneOfName {
+    SubMessage testMessage1 = 21;
+    SubMessage testMessage2 = 22;
+  }
+
+  oneof oneOfName2 {
+    SubMessage testMessage4 = 23;
+    SubMessage testMessage5 = 24;
+  }
+
+  extensions 200 to 255;
+}
+
+message TestExtension {
+  extend MainMessage {
+      optional TestExtension testExtension = 250;
+  }
+  optional string stringValue = 1;
+}
diff --git a/tests/namingstyle/test_naming_style_c.c b/tests/namingstyle/test_naming_style_c.c
new file mode 100644
index 0000000..f28bdce
--- /dev/null
+++ b/tests/namingstyle/test_naming_style_c.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "unittests.h"
+#include "naming_style.pb.h"
+
+int main()
+{
+    int status = 0;
+    main_message_t message = MAIN_MESSAGE_INIT_ZERO;
+
+    /* Verify that all members have the expected names */
+    message.lucky_number = 13;
+    message.required_number = 1;
+    message.repeated_number[0] = 1;
+    message.repeated_ints = NULL;
+
+    message.my_enum1 = MY_ENUM1_ENUM_THIRD;
+    message.my_enum2 = MY_ENUM2_ENUM2_ENTRY;
+    message.my_enum3 = MY_ENUM2_ENUM2_ENTRY;
+    message.my_enum4.arg = NULL;
+
+    message.string_values1 = NULL;
+    message.string_values2[0][0] = 'a';
+    message.optional_string.arg = NULL;
+    message.required_string[0] = 'a';
+
+    message.repeated_fixed32[0] = 1;
+
+    message.required_bytes1[0] = 0;
+    message.required_bytes2.size = 0;
+    message.repeated_bytes1_count = 0;
+    message.repeated_bytes2 = NULL;
+
+    message.has_sub_message1 = true;
+    message.sub_message1.has_test_value = true;
+    message.sub_message1.test_value = 0;
+    message.sub_message2.arg = NULL;
+    message.sub_message3.test_value = 0;
+
+    message.which_one_of_name = MAIN_MESSAGE_TEST_MESSAGE2_TAG;
+    message.one_of_name.test_message2.has_test_value = true;
+    message.one_of_name.test_message2.test_value = 5;
+
+    message.which_one_of_name2 = MAIN_MESSAGE_TEST_MESSAGE5_TAG;
+    message.test_message5.test_value = 5;
+
+    TEST(strcmp("ENTRY_FIRST", my_enum1_name(MY_ENUM1_ENTRY_FIRST)) == 0);
+
+    /* Verify that the descriptor structure is at least mostly correct
+     * by doing a round-trip encoding test.
+     */
+    {
+        uint8_t buffer1[256];
+        uint8_t buffer2[256];
+        pb_ostream_t ostream1 = pb_ostream_from_buffer(buffer1, sizeof(buffer1));
+        pb_ostream_t ostream2 = pb_ostream_from_buffer(buffer2, sizeof(buffer2));
+        pb_istream_t istream;
+        main_message_t message2 = MAIN_MESSAGE_INIT_ZERO;
+
+        TEST(pb_encode(&ostream1, &main_message_t_msg, &message));
+
+        istream = pb_istream_from_buffer(buffer1, ostream1.bytes_written);
+        TEST(pb_decode(&istream, &main_message_t_msg, &message2));
+
+        /* Encoding a second time should produce same output */
+        TEST(pb_encode(&ostream2, &main_message_t_msg, &message2));
+
+        TEST(ostream2.bytes_written == ostream1.bytes_written);
+        TEST(memcmp(buffer1, buffer2, ostream1.bytes_written) == 0);
+    }
+
+    return status;
+}
diff --git a/tests/site_scons/site_tools/nanopb.py b/tests/site_scons/site_tools/nanopb.py
index 37b8e20..4abf02a 100644
--- a/tests/site_scons/site_tools/nanopb.py
+++ b/tests/site_scons/site_tools/nanopb.py
@@ -34,6 +34,7 @@
 from SCons.Script import Dir, File
 import os.path
 import platform
+import sys
 
 try:
     warningbase = SCons.Warnings.SConsWarning