### Bug Summary

File: | builds/wireshark/wireshark/epan/protobuf_lang_tree.c
Warning: | line 1044, column 17 Potential leak of memory pointed to by 'node'

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name protobuf_lang_tree.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/epan -isystem /builds/wireshark/wireshark/build/epan -isystem /usr/include/mit-krb5 -isystem /usr/include/lua5.4 -isystem /usr/include/libxml2 -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -D epan_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/wiretap -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2026-04-26-100332-3641-1 -x c /builds/wireshark/wireshark/epan/protobuf_lang_tree.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* protobuf_lang_tree.c
2 | *
3 | * Routines of building and reading Protocol Buffers Language grammar tree.
4 | * Copyright 2019, Huang Qiangxiong <qiangxiong.huang@qq.com>
5 | *
6 | * Wireshark - Network traffic analyzer
7 | * By Gerald Combs <gerald@wireshark.org>
8 | * Copyright 1998 Gerald Combs
9 | *
10 | * SPDX-License-Identifier: GPL-2.0-or-later
11 | */
12 |
13 | #include <string.h>
14 | #include <stddef.h>
15 | #include <stdio.h>
16 | #include <stdlib.h>
17 | #include <limits.h>
18 | #include "protobuf_lang_tree.h"
19 | #include "protobuf-helper.h" /* only for PROTOBUF_TYPE_XXX enumeration */
20 |
21 | #define MAX_PROTOBUF_NODE_DEPTH100 100
22 | static bool_Bool
23 | check_node_depth(const pbl_node_t *node)
24 | {
25 | int depth = 1;
26 | for (const pbl_node_t *parent = node; parent ; parent = parent->parent) {
27 | depth++;
28 | }
29 | if (depth > MAX_PROTOBUF_NODE_DEPTH100) {
30 | return false0;
31 | }
32 | return true1;
33 | }
34 |
35 | extern void
36 | pbl_parser_error(protobuf_lang_state_t *state, const char *fmt, ...);
37 |
38 | /* Unescape string to bytes according to:
39 | *
40 | * strLit = ( { charValue } ) | ( "'" { charValue } "'" ) | ( '"' { charValue } '"' )
41 | * charValue = hexEscape | octEscape | charEscape | /[^\0\n\\]/
42 | * hexEscape = '\' ( "x" | "X" ) hexDigit hexDigit
43 | * octEscape = '\' octalDigit octalDigit octalDigit
44 | * charEscape = '\' ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | '\' | "'" | '"' )
45 | *
46 | * @param src the source escaped string terminated by '\0'
47 | * @param [out] size the length of the output byte array.
48 | * @return the unescaped byte array, should be released by g_free()
49 | */
50 | static char*
51 | protobuf_string_unescape(const char* src, int* size)
52 | {
53 | int src_len;
54 | uint8_t* dst, * q;
55 | const char* p = src;
56 |
57 | if (!(src && size && (src_len = (int)strlen(src))))
58 | return NULL((void*)0);
59 |
60 | dst = q = (uint8_t *) g_malloc0(src_len + 1);
61 |
62 | while (p < src + src_len && *p) {
63 | if (*p == '\\') {
64 | p++;
65 |
66 | if (*p == 'x' || *p == 'X') { /* unescape hex byte */
67 | *q++ = (uint8_t)strtol(p + 1, (char**)&p, 16);
68 | continue;
69 | }
70 |
71 | if (*p >= '0' && *p <= '7') { /* unescape octal byte */
72 | *q++ = (uint8_t)strtol(p, (char**)&p, 8);
73 | continue;
74 | }
75 |
76 | switch (*p)
77 | {
78 | case 'a': *q++ = '\a'; break;
79 | case 'b': *q++ = '\b'; break;
80 | case 'f': *q++ = '\f'; break;
81 | case 'n': *q++ = '\n'; break;
82 | case 'r': *q++ = '\r'; break;
83 | case 't': *q++ = '\t'; break;
84 | case 'v': *q++ = '\v'; break;
85 | default: /* include copying '\', "'" or '"' */
86 | *q++ = *p;
87 | break;
88 | }
89 | } else {
90 | *q++ = *p;
91 | }
92 | p++;
93 | }
94 | *q = 0;
95 | *size = (int)(q - dst);
96 |
97 | return (char*) dst;
98 | }
99 |
100 | /**
101 | Reinitialize the protocol buffers pool according to proto files directories.
102 | @param ppool The output descriptor_pool will be created. If *pool is not NULL, it will free it first.
103 | @param directories The root directories containing proto files. Must end with NULL element.
104 | @param error_cb The error reporter callback function.
105 | */
106 | void
107 | pbl_reinit_descriptor_pool(pbl_descriptor_pool_t** ppool, const char** directories, pbl_report_error_cb_t error_cb)
108 | {
109 | unsigned i;
110 |
111 | pbl_free_pool(*ppool);
112 | pbl_descriptor_pool_t* p = g_new0(pbl_descriptor_pool_t, 1)((pbl_descriptor_pool_t *) g_malloc0_n ((1), sizeof (pbl_descriptor_pool_t )));
113 |
114 | p->source_paths = g_queue_new();
115 | for (i = 0; directories[i] != NULL((void*)0); i++) {
116 | g_queue_push_tail(p->source_paths, g_strdup(directories[i])g_strdup_inline (directories[i]));
117 | }
118 |
119 | p->error_cb = error_cb ? error_cb : pbl_printf;
120 | p->packages = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, pbl_free_node);
121 | p->proto_files = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
122 | p->proto_files_to_be_parsed = g_queue_new();
123 |
124 | *ppool = p;
125 | }
126 |
127 | /* free all memory used by this protocol buffers language pool */
128 | void
129 | pbl_free_pool(pbl_descriptor_pool_t* pool)
130 | {
131 | if (pool == NULL((void*)0)) return;
132 |
133 | g_queue_free_full(pool->source_paths, g_free);
134 | g_hash_table_destroy(pool->packages);
135 | g_queue_free(pool->proto_files_to_be_parsed); /* elements will be removed in p->proto_files */
136 | g_hash_table_destroy(pool->proto_files);
137 |
138 | g_free(pool);
139 | }
140 |
141 | /* Canonicalize absolute file path. We only accept path like:
142 | * /home/test/protos/example.proto
143 | * D:/mydir/test/example.proto
144 | * d:\mydir\test\example.proto
145 | * This function will replace all '\' to '/', and change '//' to '/'.
146 | * May use _fullpath() in windows or realpath() in *NIX.
147 | * Return a newly-allocated path, NULL if failed (file
148 | * does not exist, or file path contains '/../').
149 | */
150 | static char*
151 | pbl_canonicalize_absolute_filepath(const char* path)
152 | {
153 | int i, j;
154 | char* canon_path = g_new(char, strlen(path) + 1)((char *) g_malloc_n ((strlen(path) + 1), sizeof (char)));
155 | /* replace all '\' to '/', and change '//' to '/' */
156 | for (i = 0, j = 0; path[i] != '\0'; i++) {
157 | if (path[i] == '\\' || path[i] == '/') {
158 | if (j > 0 && canon_path[j-1] == '/') {
159 | /* ignore redundant slash */
160 | } else {
161 | canon_path[j++] = '/';
162 | }
163 | } else {
164 | #ifdef _WIN32
165 | canon_path[j++] = g_ascii_tolower(path[i]);
166 | #else
167 | canon_path[j++] = path[i];
168 | #endif
169 | }
170 | }
171 | canon_path[j] = '\0';
172 |
173 | if (g_path_is_absolute(canon_path)
174 | && g_file_test(canon_path, G_FILE_TEST_IS_REGULAR)
175 | && strstr(canon_path, "/../") == NULL((void*)0)) {
176 | return canon_path;
177 | } else {
178 | g_free(canon_path);
179 | return NULL((void*)0);
180 | }
181 | }
182 |
183 | /* Add a file into to do list */
184 | bool_Bool
185 | pbl_add_proto_file_to_be_parsed(pbl_descriptor_pool_t* pool, const char* filepath)
186 | {
187 | char* path = NULL((void*)0);
188 | GList* it = NULL((void*)0);
189 | char* concat_path = NULL((void*)0);
190 |
191 | /* Try to get the absolute path of the file */
192 | if (g_path_is_absolute(filepath)) {
193 | path = pbl_canonicalize_absolute_filepath(filepath);
194 | }
195 |
196 | if (path == NULL((void*)0)) {
197 | /* try to concat with source directories */
198 | for (it = g_queue_peek_head_link(pool->source_paths); it; it = it->next) {
199 | concat_path = g_build_filename((char*)it->data, filepath, NULL((void*)0));
200 | path = pbl_canonicalize_absolute_filepath(concat_path);
201 | g_free(concat_path);
202 | if (path) break;
203 | }
204 | }
205 |
206 | if (path == NULL((void*)0)) {
207 | if (pool->parser_state) {
208 | /* only happened during parsing an 'import' line of a .proto file */
209 | pbl_parser_error(pool->parser_state, "file [%s] does not exist!\n", filepath);
210 | } else {
211 | /* normally happened during initializing a pool by adding files that need be loaded */
212 | pool->error_cb("Protobuf: file [%s] does not exist!\n", filepath);
213 | }
214 | return false0;
215 | }
216 |
217 | if (!g_hash_table_lookup(pool->proto_files, path)) {
218 | /* create file descriptor info */
219 | pbl_file_descriptor_t* file = g_new0(pbl_file_descriptor_t, 1)((pbl_file_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_file_descriptor_t )));
220 | file->filename = path;
221 | file->syntax_version = 2;
222 | file->package_name = PBL_DEFAULT_PACKAGE_NAME"";
223 | file->package_name_lineno = -1;
224 | file->pool = pool;
225 |
226 | /* store in hash table and list */
227 | g_hash_table_insert(pool->proto_files, path, file);
228 | g_queue_push_tail(pool->proto_files_to_be_parsed, path);
229 | } else {
230 | /* The file is already in the proto_files */
231 | g_free(path);
232 | }
233 | return true1;
234 | }
235 |
236 | /* find node according to full_name */
237 | static pbl_node_t*
238 | pbl_find_node_in_pool(const pbl_descriptor_pool_t* pool, const char* full_name, pbl_node_type_t nodetype)
239 | {
240 | char* full_name_buf;
241 | int len, i;
242 | pbl_node_t* package;
243 | pbl_node_t* node = NULL((void*)0);
244 | GSList* names = NULL((void*)0); /* NULL terminated name retrieved from full_name */
245 | GSList* it = NULL((void*)0);
246 |
247 | if (pool == NULL((void*)0) || full_name == NULL((void*)0) || pool->packages == NULL((void*)0)) {
248 | return NULL((void*)0);
249 | }
250 |
251 | if (full_name[0] == '.') {
252 | full_name++; /* skip leading dot */
253 | }
254 |
255 | full_name_buf = g_strdup(full_name)g_strdup_inline (full_name);
256 | len = (int)strlen(full_name_buf);
257 | /* scan from end to begin, and replace '.' to '\0' */
258 | for (i = len-1; i >= 0; i--) {
259 | if (full_name_buf[i] == '.' || i == 0) {
260 | if (i == 0) {
261 | /* no dot any more, we search in default package */
262 | names = g_slist_prepend(names, full_name_buf);
263 | package = (pbl_node_t*) g_hash_table_lookup(pool->packages, PBL_DEFAULT_PACKAGE_NAME"");
264 | } else { /* replace middle dot with '\0' */
265 | /* push name at top of names */
266 | names = g_slist_prepend(names, full_name_buf + i + 1);
267 | full_name_buf[i] = 0;
268 | /* take 0~i of full_name_buf as package name */
269 | package = (pbl_node_t*) g_hash_table_lookup(pool->packages, full_name_buf);
270 | }
271 | if (package) {
272 | node = package;
273 | /* search node in this package */
274 | for (it = names; (it && node && node->children_by_name); it = it->next) {
275 | node = (pbl_node_t*) g_hash_table_lookup(node->children_by_name, it->data);
276 | }
277 |
278 | if (it == NULL((void*)0) && node && node->nodetype == nodetype) {
279 | break; /* found */
280 | }
281 | node = NULL((void*)0);
282 | }
283 | }
284 | }
285 |
286 | if (names) {
287 | g_slist_free(names);
288 | }
289 | g_free(full_name_buf);
290 | return node;
291 | }
292 |
293 | /* get the full name of node. if it is NULL, it will be built. */
294 | const char*
295 | // NOLINTNEXTLINE(misc-no-recursion)
296 | pbl_get_node_full_name(pbl_node_t* node)
297 | {
298 | const char* parent_full_name;
299 | if (node == NULL((void*)0)
300 | || node->nodetype == PBL_UNKNOWN
301 | || node->nodetype == PBL_OPTIONS
302 | || node->nodetype == PBL_OPTION) {
303 | return NULL((void*)0);
304 | }
305 |
306 | if (node->full_name) {
307 | return node->full_name;
308 | }
309 |
310 | if (node->nodetype == PBL_ONEOF) {
311 | if (!check_node_depth(node)) {
312 | return NULL((void*)0);
313 | }
314 | return pbl_get_node_full_name(node->parent);
315 | }
316 |
317 | if (node->nodetype == PBL_PACKAGE) {
318 | node->full_name = g_strdup(node->name)g_strdup_inline (node->name);
319 | } else {
320 | parent_full_name = pbl_get_node_full_name(node->parent);
321 | if (parent_full_name && parent_full_name[0] != 0) {
322 | node->full_name = g_strconcat(parent_full_name, ".", node->name, NULL((void*)0));
323 | } else {
324 | node->full_name = g_strdup(node->name)g_strdup_inline (node->name);
325 | }
326 | }
327 |
328 | return node->full_name;
329 | }
330 |
331 | /* try to find node globally or in the context or parents (message or package) of the context */
332 | static const pbl_node_t*
333 | pbl_find_node_in_context(const pbl_node_t* context, const char* name, pbl_node_type_t nodetype)
334 | {
335 | const pbl_node_t* node = NULL((void*)0);
336 | pbl_descriptor_pool_t* pool = NULL((void*)0);
337 | char* parent_name;
338 | char* full_name;
339 |
340 | if (context == NULL((void*)0) || name == NULL((void*)0)) {
341 | return NULL((void*)0);
342 | }
343 |
344 | if (name[0] == '.') {
345 | /* A leading '.' (for example, .foo.bar.Baz) means to start from the outermost scope. */
346 | if (context->file && context->file->pool) {
347 | return pbl_find_node_in_pool(context->file->pool, name, nodetype);
348 | } else {
349 | return NULL((void*)0);
350 | }
351 | }
352 |
353 | /* find pool */
354 | if (context->file) {
355 | pool = context->file->pool;
356 | }
357 |
358 | /* try find node in the context or parents (message or package) of the context */
359 | if (pool) {
360 | int remaining;
361 | parent_name = g_strdup(pbl_get_node_full_name((pbl_node_t*) context))g_strdup_inline (pbl_get_node_full_name((pbl_node_t*) context ));
362 | remaining = (int)strlen(parent_name);
363 | while (remaining > 0) {
364 | full_name = g_strconcat(parent_name, ".", name, NULL((void*)0));
365 | node = pbl_find_node_in_pool(pool, full_name, nodetype);
366 | g_free(full_name);
367 | if (node) {
368 | g_free(parent_name);
369 | return node;
370 | }
371 | /* scan from end to begin, and replace first '.' to '\0' */
372 | for (remaining--; remaining > 0; remaining--) {
373 | if (parent_name[remaining] == '.') {
374 | /* found a potential parent node name */
375 | parent_name[remaining] = '\0';
376 | break; /* break from the 'for' loop, continue 'while' loop */
377 | }
378 | }
379 | }
380 | g_free(parent_name);
381 |
382 | /* try find node in pool directly */
383 | return pbl_find_node_in_pool(pool, name, nodetype);
384 | }
385 |
386 | return NULL((void*)0);
387 | }
388 |
389 | /* like descriptor_pool::FindMethodByName */
390 | const pbl_method_descriptor_t*
391 | pbl_message_descriptor_pool_FindMethodByName(const pbl_descriptor_pool_t* pool, const char* full_name)
392 | {
393 | pbl_node_t* n = pbl_find_node_in_pool(pool, full_name, PBL_METHOD);
394 | return n ? (pbl_method_descriptor_t*)n : NULL((void*)0);
395 | }
396 |
397 | /* like MethodDescriptor::name() */
398 | const char*
399 | pbl_method_descriptor_name(const pbl_method_descriptor_t* method)
400 | {
401 | return pbl_get_node_name((pbl_node_t*)method);
402 | }
403 |
404 | /* like MethodDescriptor::full_name() */
405 | const char*
406 | pbl_method_descriptor_full_name(const pbl_method_descriptor_t* method)
407 | {
408 | return pbl_get_node_full_name((pbl_node_t*)method);
409 | }
410 |
411 | /* like MethodDescriptor::input_type() */
412 | const pbl_message_descriptor_t*
413 | pbl_method_descriptor_input_type(const pbl_method_descriptor_t* method)
414 | {
415 | const pbl_node_t* n = pbl_find_node_in_context((pbl_node_t*)method, method->in_msg_type, PBL_MESSAGE);
416 | return n ? (const pbl_message_descriptor_t*)n : NULL((void*)0);
417 | }
418 |
419 | /* like MethodDescriptor::output_type() */
420 | const pbl_message_descriptor_t*
421 | pbl_method_descriptor_output_type(const pbl_method_descriptor_t* method)
422 | {
423 | const pbl_node_t* n = pbl_find_node_in_context((pbl_node_t*)method, method->out_msg_type, PBL_MESSAGE);
424 | return n ? (const pbl_message_descriptor_t*)n : NULL((void*)0);
425 | }
426 |
427 | /* like descriptor_pool::FindMessageTypeByName() */
428 | const pbl_message_descriptor_t*
429 | pbl_message_descriptor_pool_FindMessageTypeByName(const pbl_descriptor_pool_t* pool, const char* name)
430 | {
431 | pbl_node_t* n = pbl_find_node_in_pool(pool, name, PBL_MESSAGE);
432 | return n ? (pbl_message_descriptor_t*)n : NULL((void*)0);
433 | }
434 |
435 | /* like Descriptor::name() */
436 | const char*
437 | pbl_message_descriptor_name(const pbl_message_descriptor_t* message)
438 | {
439 | return pbl_get_node_name((pbl_node_t*)message);
440 | }
441 |
442 | /* like Descriptor::full_name() */
443 | const char*
444 | pbl_message_descriptor_full_name(const pbl_message_descriptor_t* message)
445 | {
446 | return pbl_get_node_full_name((pbl_node_t*)message);
447 | }
448 |
449 | /* like Descriptor::field_count() */
450 | int
451 | pbl_message_descriptor_field_count(const pbl_message_descriptor_t* message)
452 | {
453 | return (message && message->fields) ? g_queue_get_length(message->fields) : 0;
454 | }
455 |
456 | /* like Descriptor::field() */
457 | const pbl_field_descriptor_t*
458 | pbl_message_descriptor_field(const pbl_message_descriptor_t* message, int field_index)
459 | {
460 | return (message && message->fields) ? (pbl_field_descriptor_t*) g_queue_peek_nth(message->fields, field_index) : NULL((void*)0);
461 | }
462 |
463 | /* like Descriptor::FindFieldByNumber() */
464 | const pbl_field_descriptor_t*
465 | pbl_message_descriptor_FindFieldByNumber(const pbl_message_descriptor_t* message, int number)
466 | {
467 | if (message && message->fields_by_number) {
468 | return (pbl_field_descriptor_t*) g_hash_table_lookup(message->fields_by_number, GINT_TO_POINTER(number)((gpointer) (glong) (number)));
469 | } else {
470 | return NULL((void*)0);
471 | }
472 | }
473 |
474 | /* like Descriptor::FindFieldByName() */
475 | const pbl_field_descriptor_t*
476 | pbl_message_descriptor_FindFieldByName(const pbl_message_descriptor_t* message, const char* name)
477 | {
478 | if (message && ((pbl_node_t*)message)->children_by_name) {
479 | return (pbl_field_descriptor_t*) g_hash_table_lookup(((pbl_node_t*)message)->children_by_name, name);
480 | } else {
481 | return NULL((void*)0);
482 | }
483 | }
484 |
485 | /* like FieldDescriptor::full_name() */
486 | const char*
487 | pbl_field_descriptor_full_name(const pbl_field_descriptor_t* field)
488 | {
489 | return pbl_get_node_full_name((pbl_node_t*)field);
490 | }
491 |
492 | /* like FieldDescriptor::name() */
493 | const char*
494 | pbl_field_descriptor_name(const pbl_field_descriptor_t* field)
495 | {
496 | return pbl_get_node_name((pbl_node_t*)field);
497 | }
498 |
499 | /* like FieldDescriptor::number() */
500 | int
501 | pbl_field_descriptor_number(const pbl_field_descriptor_t* field)
502 | {
503 | return GPOINTER_TO_INT(field->number)((gint) (glong) (field->number));
504 | }
505 |
506 | /* like FieldDescriptor::type() */
507 | int
508 | pbl_field_descriptor_type(const pbl_field_descriptor_t* field)
509 | {
510 | const pbl_node_t* node;
511 | if (field->type == PROTOBUF_TYPE_NONE) {
512 | /* try to lookup as ENUM */
513 | node = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_ENUM);
514 | if (node) {
515 | ((pbl_field_descriptor_t*)field)->type = PROTOBUF_TYPE_ENUM;
516 | } else {
517 | /* try to lookup as MESSAGE */
518 | node = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_MESSAGE);
519 | if (node) {
520 | ((pbl_field_descriptor_t*)field)->type = PROTOBUF_TYPE_MESSAGE;
521 | }
522 | }
523 | }
524 | return field->type;
525 | }
526 |
527 | /* like FieldDescriptor::is_repeated() */
528 | int
529 | pbl_field_descriptor_is_repeated(const pbl_field_descriptor_t* field)
530 | {
531 | return field->is_repeated ? 1 : 0;
532 | }
533 |
534 | /* like FieldDescriptor::is_packed() */
535 | int
536 | pbl_field_descriptor_is_packed(const pbl_field_descriptor_t* field)
537 | {
538 | bool_Bool has_packed_option;
539 | bool_Bool packed_option_value;
540 | int syntax_version = ((pbl_node_t*)field)->file->syntax_version;
541 |
542 | /* determine packed flag */
543 | if (field->is_repeated == false0) {
544 | return false0;
545 | }
546 | /* note: field->type may be undetermined until calling pbl_field_descriptor_type() */
547 | switch (pbl_field_descriptor_type(field)) {
548 | case PROTOBUF_TYPE_STRING:
549 | case PROTOBUF_TYPE_GROUP:
550 | case PROTOBUF_TYPE_MESSAGE:
551 | case PROTOBUF_TYPE_BYTES:
552 | return false0;
553 | default: /* only repeated fields of primitive numeric types can be declared "packed". */
554 | has_packed_option = field->options_node
555 | && field->options_node->children_by_name
556 | && g_hash_table_lookup(field->options_node->children_by_name, "packed");
557 |
558 | packed_option_value = (has_packed_option ?
559 | g_strcmp0(
560 | ((pbl_option_descriptor_t*)g_hash_table_lookup(
561 | field->options_node->children_by_name, "packed"))->value, "true") == 0
562 | : false0);
563 |
564 | if (syntax_version == 2) {
565 | return packed_option_value;
566 | } else { /* packed default in syntax_version = 3 */
567 | return has_packed_option ? packed_option_value : true1;
568 | }
569 | }
570 | }
571 |
572 | /* like FieldDescriptor::TypeName() */
573 | const char*
574 | pbl_field_descriptor_TypeName(wmem_allocator_t* scope, int field_type)
575 | {
576 | return val_to_str(scope, field_type, protobuf_field_type, "UNKNOWN_FIELD_TYPE(%d)");
577 | }
578 |
579 | /* like FieldDescriptor::message_type() type = TYPE_MESSAGE or TYPE_GROUP */
580 | const pbl_message_descriptor_t*
581 | pbl_field_descriptor_message_type(const pbl_field_descriptor_t* field)
582 | {
583 | const pbl_node_t* n;
584 | if (field->type == PROTOBUF_TYPE_MESSAGE || field->type == PROTOBUF_TYPE_GROUP) {
585 | n = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_MESSAGE);
586 | return n ? (const pbl_message_descriptor_t*)n : NULL((void*)0);
587 | }
588 | return NULL((void*)0);
589 | }
590 |
591 | /* like FieldDescriptor::enum_type() type = TYPE_ENUM */
592 | const pbl_enum_descriptor_t*
593 | pbl_field_descriptor_enum_type(const pbl_field_descriptor_t* field)
594 | {
595 | const pbl_node_t* n;
596 | if (field->type == PROTOBUF_TYPE_ENUM) {
597 | n = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_ENUM);
598 | return n ? (const pbl_enum_descriptor_t*)n : NULL((void*)0);
599 | }
600 | return NULL((void*)0);
601 | }
602 |
603 | /* like FieldDescriptor::is_required() */
604 | bool_Bool
605 | pbl_field_descriptor_is_required(const pbl_field_descriptor_t* field)
606 | {
607 | return field->is_required;
608 | }
609 |
610 | /* like FieldDescriptor::has_default_value() */
611 | bool_Bool
612 | pbl_field_descriptor_has_default_value(const pbl_field_descriptor_t* field)
613 | {
614 | return field->has_default_value;
615 | }
616 |
617 | /* like FieldDescriptor::default_value_int32() */
618 | int32_t
619 | pbl_field_descriptor_default_value_int32(const pbl_field_descriptor_t* field)
620 | {
621 | return field->default_value.i32;
622 | }
623 |
624 | /* like FieldDescriptor::default_value_int64() */
625 | int64_t
626 | pbl_field_descriptor_default_value_int64(const pbl_field_descriptor_t* field)
627 | {
628 | return field->default_value.i64;
629 | }
630 |
631 | /* like FieldDescriptor::default_value_uint32() */
632 | uint32_t
633 | pbl_field_descriptor_default_value_uint32(const pbl_field_descriptor_t* field)
634 | {
635 | return field->default_value.u32;
636 | }
637 |
638 | /* like FieldDescriptor::default_value_uint64() */
639 | uint64_t
640 | pbl_field_descriptor_default_value_uint64(const pbl_field_descriptor_t* field)
641 | {
642 | return field->default_value.u64;
643 | }
644 |
645 | /* like FieldDescriptor::default_value_float() */
646 | float
647 | pbl_field_descriptor_default_value_float(const pbl_field_descriptor_t* field)
648 | {
649 | return field->default_value.f;
650 | }
651 |
652 | /* like FieldDescriptor::default_value_double() */
653 | double
654 | pbl_field_descriptor_default_value_double(const pbl_field_descriptor_t* field)
655 | {
656 | return field->default_value.d;
657 | }
658 |
659 | /* like FieldDescriptor::default_value_bool() */
660 | bool_Bool
661 | pbl_field_descriptor_default_value_bool(const pbl_field_descriptor_t* field)
662 | {
663 | return field->default_value.b;
664 | }
665 |
666 | /* like FieldDescriptor::default_value_string() */
667 | const char*
668 | pbl_field_descriptor_default_value_string(const pbl_field_descriptor_t* field, int* size)
669 | {
670 | *size = field->string_or_bytes_default_value_length;
671 | return field->default_value.s;
672 | }
673 |
674 | /* like FieldDescriptor::default_value_enum() */
675 | const pbl_enum_value_descriptor_t*
676 | pbl_field_descriptor_default_value_enum(const pbl_field_descriptor_t* field)
677 | {
678 | const pbl_enum_descriptor_t* enum_desc;
679 |
680 | if (pbl_field_descriptor_type(field) == PROTOBUF_TYPE_ENUM
681 | && field->default_value.e == NULL((void*)0) && (enum_desc = pbl_field_descriptor_enum_type(field))) {
682 |
683 | if (field->orig_default_value) {
684 | /* find enum_value according to the name of default value */
685 | ((pbl_field_descriptor_t*)field)->default_value.e = pbl_enum_descriptor_FindValueByName(enum_desc, field->orig_default_value);
686 | } else {
687 | /* the default value is the first defined enum value */
688 | ((pbl_field_descriptor_t*)field)->default_value.e = pbl_enum_descriptor_value(enum_desc, 0);
689 | }
690 | }
691 |
692 | return field->default_value.e;
693 | }
694 |
695 | /* like EnumDescriptor::name() */
696 | const char*
697 | pbl_enum_descriptor_name(const pbl_enum_descriptor_t* anEnum)
698 | {
699 | return pbl_get_node_name((pbl_node_t*)anEnum);
700 | }
701 |
702 | /* like EnumDescriptor::full_name() */
703 | const char*
704 | pbl_enum_descriptor_full_name(const pbl_enum_descriptor_t* anEnum)
705 | {
706 | return pbl_get_node_full_name((pbl_node_t*)anEnum);
707 | }
708 |
709 | /* like EnumDescriptor::value_count() */
710 | int
711 | pbl_enum_descriptor_value_count(const pbl_enum_descriptor_t* anEnum)
712 | {
713 | return (anEnum && anEnum->values) ? g_queue_get_length(anEnum->values) : 0;
714 | }
715 |
716 | /* like EnumDescriptor::value() */
717 | const pbl_enum_value_descriptor_t*
718 | pbl_enum_descriptor_value(const pbl_enum_descriptor_t* anEnum, int value_index)
719 | {
720 | return (anEnum && anEnum->values) ? (pbl_enum_value_descriptor_t*) g_queue_peek_nth(anEnum->values, value_index) : NULL((void*)0);
721 | }
722 |
723 | /* like EnumDescriptor::FindValueByNumber() */
724 | const pbl_enum_value_descriptor_t*
725 | pbl_enum_descriptor_FindValueByNumber(const pbl_enum_descriptor_t* anEnum, int number)
726 | {
727 | if (anEnum && anEnum->values_by_number) {
728 | return (pbl_enum_value_descriptor_t*) g_hash_table_lookup(anEnum->values_by_number, GINT_TO_POINTER(number)((gpointer) (glong) (number)));
729 | } else {
730 | return NULL((void*)0);
731 | }
732 | }
733 |
734 | /* like EnumDescriptor::FindValueByName() */
735 | const pbl_enum_value_descriptor_t*
736 | pbl_enum_descriptor_FindValueByName(const pbl_enum_descriptor_t* anEnum, const char* name)
737 | {
738 | if (anEnum && ((pbl_node_t*)anEnum)->children_by_name) {
739 | return (pbl_enum_value_descriptor_t*)g_hash_table_lookup(((pbl_node_t*)anEnum)->children_by_name, name);
740 | } else {
741 | return NULL((void*)0);
742 | }
743 | }
744 |
745 | /* like EnumValueDescriptor::name() */
746 | const char*
747 | pbl_enum_value_descriptor_name(const pbl_enum_value_descriptor_t* enumValue)
748 | {
749 | return pbl_get_node_name((pbl_node_t*)enumValue);
750 | }
751 |
752 | /* like EnumValueDescriptor::full_name() */
753 | const char*
754 | pbl_enum_value_descriptor_full_name(const pbl_enum_value_descriptor_t* enumValue)
755 | {
756 | return pbl_get_node_full_name((pbl_node_t*)enumValue);
757 | }
758 |
759 | /* like EnumValueDescriptor::number() */
760 | int
761 | pbl_enum_value_descriptor_number(const pbl_enum_value_descriptor_t* enumValue)
762 | {
763 | return GPOINTER_TO_INT(enumValue->number)((gint) (glong) (enumValue->number));
764 | }
765 |
766 | static void
767 | // NOLINTNEXTLINE(misc-no-recursion)
768 | pbl_traverse_sub_tree(const pbl_node_t* node, void (*cb)(const pbl_message_descriptor_t*, void*), void* userdata)
769 | {
770 | GList* it;
771 | if (node == NULL((void*)0)) {
772 | return;
773 | }
774 |
775 | if (node->nodetype == PBL_MESSAGE) {
776 | (*cb)((const pbl_message_descriptor_t*) node, userdata);
777 | }
778 |
779 | if (node->children) {
780 | if (!check_node_depth(node)) {
781 | return;
782 | }
783 | for (it = g_queue_peek_head_link(node->children); it; it = it->next) {
784 | pbl_traverse_sub_tree((const pbl_node_t*) it->data, cb, userdata);
785 | }
786 | }
787 | }
788 |
789 | /* visit all message in this pool */
790 | void
791 | pbl_foreach_message(const pbl_descriptor_pool_t* pool, void (*cb)(const pbl_message_descriptor_t*, void*), void* userdata)
792 | {
793 | GHashTableIter it;
794 | void *key, *value;
795 | g_hash_table_iter_init (&it, pool->packages);
796 | while (g_hash_table_iter_next (&it, &key, &value)) {
797 | pbl_traverse_sub_tree((const pbl_node_t*)value, cb, userdata);
798 | }
799 | }
800 |
801 |
802 | /*
803 | * Following are tree building functions that should only be invoked by protobuf_lang parser.
804 | */
805 |
806 | static void
807 | pbl_init_node(pbl_node_t* node, pbl_file_descriptor_t* file, int lineno, pbl_node_type_t nodetype, const char* name)
808 | {
809 | node->nodetype = nodetype;
810 | node->name = g_strdup(name)g_strdup_inline (name);
811 | node->file = file;
812 | node->lineno = (lineno > -1) ? lineno : -1;
813 | }
814 |
815 | /* create a normal node */
816 | pbl_node_t*
817 | pbl_create_node(pbl_file_descriptor_t* file, int lineno, pbl_node_type_t nodetype, const char* name)
818 | {
819 | pbl_node_t* node = NULL((void*)0);
820 |
821 | switch (nodetype) {
8 | ← | Control jumps to 'case PBL_MESSAGE:' at line 828 | →
822 | case PBL_METHOD: /* should use pbl_create_method_node() */
823 | case PBL_FIELD: /* should use pbl_create_field_node() */
824 | case PBL_MAP_FIELD: /* should use pbl_create_map_field_node() */
825 | case PBL_ENUM_VALUE: /* should use pbl_create_enum_value_node() */
826 | case PBL_OPTION: /* should use pbl_create_option_node() */
827 | return NULL((void*)0);
828 | case PBL_MESSAGE:
829 | node = (pbl_node_t*) g_malloc0(sizeof(pbl_message_descriptor_t));
9 | ← | Memory is allocated | →
830 | break;
10 | ← | Execution continues on line 837 | →
831 | case PBL_ENUM:
832 | node = (pbl_node_t*) g_malloc0(sizeof(pbl_enum_descriptor_t));
833 | break;
834 | default:
835 | node = g_new0(pbl_node_t, 1)((pbl_node_t *) g_malloc0_n ((1), sizeof (pbl_node_t)));
836 | }
837 | pbl_init_node(node, file, lineno, nodetype, name);
838 | return node;
839 | }
840 |
841 | pbl_node_t*
842 | pbl_set_node_name(pbl_node_t* node, int lineno, const char* newname)
843 | {
844 | g_free(node->name);
845 | node->name = g_strdup(newname)g_strdup_inline (newname);
846 | if (lineno > -1) {
847 | node->lineno = lineno;
848 | }
849 | return node;
850 | }
851 |
852 | static pbl_option_descriptor_t*
853 | pbl_get_option_by_name(pbl_node_t* options, const char* name)
854 | {
855 | if (options && options->children_by_name) {
856 | return (pbl_option_descriptor_t*)g_hash_table_lookup(options->children_by_name, name);
857 | } else {
858 | return NULL((void*)0);
859 | }
860 | }
861 |
862 | /* create a method (rpc or stream of service) node */
863 | pbl_node_t* pbl_create_method_node(pbl_file_descriptor_t* file, int lineno,
864 | const char* name, const char* in_msg_type,
865 | bool_Bool in_is_stream, const char* out_msg_type, bool_Bool out_is_stream)
866 | {
867 | pbl_method_descriptor_t* node = g_new0(pbl_method_descriptor_t, 1)((pbl_method_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_method_descriptor_t )));
868 | pbl_init_node(&node->basic_info, file, lineno, PBL_METHOD, name);
869 |
870 | node->in_msg_type = g_strdup(in_msg_type)g_strdup_inline (in_msg_type);
871 | node->in_is_stream = in_is_stream;
872 | node->out_msg_type = g_strdup(out_msg_type)g_strdup_inline (out_msg_type);
873 | node->out_is_stream = out_is_stream;
874 |
875 | return (pbl_node_t*)node;
876 | }
877 |
878 | /* Get type simple type enum value according to the type name.
879 | Return 0 means undetermined. */
880 | static int
881 | pbl_get_simple_type_enum_value_by_typename(const char* type_name)
882 | {
883 | int i = str_to_val(type_name, protobuf_field_type, 0);
884 | if (i == PROTOBUF_TYPE_GROUP || i == PROTOBUF_TYPE_MESSAGE || i == PROTOBUF_TYPE_ENUM) {
885 | i = PROTOBUF_TYPE_NONE; /* complex type will find after parsing */
886 | }
887 |
888 | return i;
889 | }
890 |
891 | /* create a field node */
892 | pbl_node_t* pbl_create_field_node(pbl_file_descriptor_t* file, int lineno, const char* label,
893 | const char* type_name, const char* name, int number, pbl_node_t* options)
894 | {
895 | pbl_option_descriptor_t* default_option;
896 | pbl_field_descriptor_t* node = g_new0(pbl_field_descriptor_t, 1)((pbl_field_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_field_descriptor_t )));
897 | pbl_init_node(&node->basic_info, file, lineno, PBL_FIELD, name);
898 |
899 | node->number = number;
900 | node->options_node = options;
901 | node->is_repeated = (g_strcmp0(label, "repeated") == 0);
902 | node->type_name = g_strdup(type_name)g_strdup_inline (type_name);
903 | /* type 0 means undetermined, it will be determined on
904 | calling pbl_field_descriptor_type() later */
905 | node->type = pbl_get_simple_type_enum_value_by_typename(type_name);
906 | node->is_required = (g_strcmp0(label, "required") == 0);
907 |
908 | /* Try to get default value for proto2.
909 | * There is nothing to do for proto3, because the default value
910 | * is zero or false in proto3.
911 | */
912 | default_option = pbl_get_option_by_name(options, "default");
913 | if (default_option && default_option->value) {
914 | node->has_default_value = true1;
915 | node->orig_default_value = g_strdup(default_option->value)g_strdup_inline (default_option->value);
916 | /* get default value for simple type */
917 | switch (node->type)
918 | {
919 | case PROTOBUF_TYPE_INT32:
920 | case PROTOBUF_TYPE_SINT32:
921 | case PROTOBUF_TYPE_SFIXED32:
922 | sscanf(node->orig_default_value, "%" SCNd32"d", &node->default_value.i32);
923 | break;
924 |
925 | case PROTOBUF_TYPE_INT64:
926 | case PROTOBUF_TYPE_SINT64:
927 | case PROTOBUF_TYPE_SFIXED64:
928 | node->default_value.i64 = g_ascii_strtoll(node->orig_default_value, NULL((void*)0), 10);
929 | break;
930 |
931 | case PROTOBUF_TYPE_UINT32:
932 | case PROTOBUF_TYPE_FIXED32:
933 | sscanf(node->orig_default_value, "%" SCNu32"u", &node->default_value.u32);
934 | break;
935 |
936 | case PROTOBUF_TYPE_UINT64:
937 | case PROTOBUF_TYPE_FIXED64:
938 | node->default_value.u64 = g_ascii_strtoull(node->orig_default_value, NULL((void*)0), 10);
939 | break;
940 |
941 | case PROTOBUF_TYPE_BOOL:
942 | node->default_value.b = (g_strcmp0(node->orig_default_value, "true") == 0);
943 | break;
944 |
945 | case PROTOBUF_TYPE_DOUBLE:
946 | node->default_value.d = g_ascii_strtod(node->orig_default_value, NULL((void*)0));
947 | break;
948 |
949 | case PROTOBUF_TYPE_FLOAT:
950 | node->default_value.f = (float) g_ascii_strtod(node->orig_default_value, NULL((void*)0));
951 | break;
952 |
953 | case PROTOBUF_TYPE_STRING:
954 | case PROTOBUF_TYPE_BYTES:
955 | node->default_value.s = protobuf_string_unescape(node->orig_default_value, &node->string_or_bytes_default_value_length);
956 | break;
957 |
958 | default:
959 | /* The default value of ENUM type will be generated
960 | * in pbl_field_descriptor_default_value_enum().
961 | * Message or group will be ignore.
962 | */
963 | break;
964 | }
965 | }
966 |
967 | return (pbl_node_t*)node;
968 | }
969 |
970 | /* create a map field node */
971 | pbl_node_t* pbl_create_map_field_node(pbl_file_descriptor_t* file, int lineno,
972 | const char* name, int number, pbl_node_t* options)
973 | {
974 | pbl_field_descriptor_t* node = g_new0(pbl_field_descriptor_t, 1)((pbl_field_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_field_descriptor_t )));
975 | pbl_init_node(&node->basic_info, file, lineno, PBL_MAP_FIELD, name);
976 |
977 | node->number = number;
978 | node->type_name = g_strconcat(name, "MapEntry", NULL((void*)0));
979 | node->type = PROTOBUF_TYPE_MESSAGE;
980 | node->is_repeated = true1;
981 | node->options_node = options;
982 |
983 | return (pbl_node_t*)node;
984 | }
985 |
986 | /* create an enumeration field node */
987 | pbl_node_t*
988 | pbl_create_enum_value_node(pbl_file_descriptor_t* file, int lineno, const char* name, int number)
989 | {
990 | pbl_enum_value_descriptor_t* node = g_new0(pbl_enum_value_descriptor_t, 1)((pbl_enum_value_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_enum_value_descriptor_t )));
991 | pbl_init_node(&node->basic_info, file, lineno, PBL_ENUM_VALUE, name);
992 |
993 | node->number = number;
994 | return (pbl_node_t*)node;
995 | }
996 |
997 | /* create an option node */
998 | pbl_node_t* pbl_create_option_node(pbl_file_descriptor_t* file, int lineno,
999 | const char* name, const char* value)
1000 | {
1001 | pbl_option_descriptor_t* node = g_new0(pbl_option_descriptor_t, 1)((pbl_option_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_option_descriptor_t )));
1002 | pbl_init_node(&node->basic_info, file, lineno, PBL_OPTION, name);
1003 |
1004 | if (value)
1005 | node->value = g_strdup(value)g_strdup_inline (value);
1006 | return (pbl_node_t*)node;
1007 | }
1008 |
1009 | /* add a node as a child of parent node, and return the parent pointer */
1010 | pbl_node_t*
1011 | // NOLINTNEXTLINE(misc-no-recursion)
1012 | pbl_add_child(pbl_node_t* parent, pbl_node_t* child)
1013 | {
1014 | pbl_node_t* node = NULL((void*)0);
1015 | if (child
12.1 | 'child' is not equal to NULL
== NULL((void*)0) || parent
12.2 | 'parent' is not equal to NULL
== NULL((void*)0)) {
1 | Assuming 'child' is not equal to NULL | →
2 | ← | Assuming 'parent' is not equal to NULL | →
3 | ← | Taking false branch | →
13 | ← | Taking false branch | →
1016 | return parent;
1017 | }
1018 |
1019 | if (!check_node_depth(parent)) {
4 | ← | Taking false branch | →
14 | ← | Assuming the condition is true | →
15 | ← | Taking true branch | →
1020 | return NULL((void*)0);
16 | ← | Returning without deallocating memory or storing the pointer for later deallocation | →
1021 | }
1022 |
1023 | /* add a message node for mapField first */
1024 | if (child->nodetype == PBL_MAP_FIELD) {
5 | ← | Assuming field 'nodetype' is equal to PBL_MAP_FIELD | →
6 | ← | Taking true branch | →
1025 | node = pbl_create_node(child->file, child->lineno, PBL_MESSAGE, ((pbl_field_descriptor_t*)child)->type_name);
7 | ← | Calling 'pbl_create_node' | →
11 | ← | Returned allocated memory | →
1026 | pbl_merge_children(node, child);
1027 | pbl_add_child(parent, node);
12 | ← | Calling 'pbl_add_child' | →
17 | ← | Returning from 'pbl_add_child' | →
1028 | }
1029 |
1030 | child->parent = parent;
1031 |
1032 | /* add child to children list */
1033 | if (parent->children == NULL((void*)0)) {
18 | ← | Assuming field 'children' is not equal to NULL | →
19 | ← | Taking false branch | →
1034 | parent->children = g_queue_new();
1035 | }
1036 | g_queue_push_tail(parent->children, child);
1037 |
1038 | /* add child to children_by_name table */
1039 | if (parent->children_by_name == NULL((void*)0)) {
20 | ← | Assuming field 'children_by_name' is not equal to NULL | →
21 | ← | Taking false branch | →
1040 | parent->children_by_name = g_hash_table_new_full(g_str_hash, g_str_equal, NULL((void*)0), NULL((void*)0));
1041 | }
1042 |
1043 | node = (pbl_node_t*) g_hash_table_lookup(parent->children_by_name, child->name);
1044 | if (node && node->nodetype == PBL_OPTION && child->nodetype == PBL_OPTION
22 | ← | Assuming 'node' is non-null | →
23 | ← | Potential leak of memory pointed to by 'node'
1045 | && ((pbl_option_descriptor_t*)node)->value && ((pbl_option_descriptor_t*)child)->value) {
1046 | /* repeated option can be set many times like:
1047 | string fieldWithComplexOption5 = 5 [(rules).repeated_int = 1, (rules).repeated_int = 2];
1048 | we just merge the old value and new value in format /old_value "," new_value/.
1049 | */
1050 | char* oval = ((pbl_option_descriptor_t*)node)->value;
1051 | char* nval = ((pbl_option_descriptor_t*)child)->value;
1052 | ((pbl_option_descriptor_t*)child)->value = g_strconcat(oval, ",", nval, NULL((void*)0));
1053 | g_free(nval);
1054 | } else if (node && child->file && parent->file
1055 | && child->file->pool && child->file->pool->error_cb
1056 | /* Let's assume that any set of base types we point at are valid.. */
1057 | && !strstr(node->file->filename, "google")) {
1058 | child->file->pool->error_cb(
1059 | "Protobuf: Warning: \"%s\" of [%s:%d] is already defined in file [%s:%d].\n",
1060 | child->name, child->file->filename, child->lineno, node->file->filename, node->lineno);
1061 | }
1062 |
1063 | g_hash_table_insert(parent->children_by_name, child->name, child);
1064 |
1065 | if (parent->nodetype == PBL_MESSAGE) {
1066 | pbl_message_descriptor_t* msg = (pbl_message_descriptor_t*) parent;
1067 |
1068 | /* add child to fields_by_number table */
1069 | if (child->nodetype == PBL_FIELD || child->nodetype == PBL_MAP_FIELD) {
1070 | if (msg->fields == NULL((void*)0)) {
1071 | msg->fields = g_queue_new();
1072 | }
1073 | g_queue_push_tail(msg->fields, child);
1074 |
1075 | if (msg->fields_by_number == NULL((void*)0)) {
1076 | msg->fields_by_number = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL((void*)0), NULL((void*)0));
1077 | }
1078 | g_hash_table_insert(msg->fields_by_number,
1079 | GINT_TO_POINTER(((pbl_field_descriptor_t*)child)->number)((gpointer) (glong) (((pbl_field_descriptor_t*)child)->number )), child);
1080 | }
1081 |
1082 | } else if (parent->nodetype == PBL_ENUM && child->nodetype == PBL_ENUM_VALUE) {
1083 | pbl_enum_descriptor_t* anEnum = (pbl_enum_descriptor_t*) parent;
1084 |
1085 | if (anEnum->values == NULL((void*)0)) {
1086 | anEnum->values = g_queue_new();
1087 | }
1088 | g_queue_push_tail(anEnum->values, child);
1089 |
1090 | /* add child to values_by_number table */
1091 | if (anEnum->values_by_number == NULL((void*)0)) {
1092 | anEnum->values_by_number = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL((void*)0), NULL((void*)0));
1093 | }
1094 | g_hash_table_insert(anEnum->values_by_number,
1095 | GINT_TO_POINTER(((pbl_enum_value_descriptor_t*)child)->number)((gpointer) (glong) (((pbl_enum_value_descriptor_t*)child)-> number)), child);
1096 | }
1097 |
1098 | return parent;
1099 | }
1100 |
1101 | /* merge one('from') node's children to another('to') node, and return the 'to' pointer */
1102 | pbl_node_t*
1103 | // NOLINTNEXTLINE(misc-no-recursion)
1104 | pbl_merge_children(pbl_node_t* to, pbl_node_t* from)
1105 | {
1106 | GList* it;
1107 | pbl_node_t* child;
1108 |
1109 | if (to == NULL((void*)0) || from == NULL((void*)0)) {
1110 | return to;
1111 | }
1112 |
1113 | if (from->children) {
1114 | for (it = g_queue_peek_head_link(from->children); it; it = it->next) {
1115 | child = (pbl_node_t*)it->data;
1116 | pbl_add_child(to, child);
1117 | }
1118 |
1119 | g_queue_free(from->children);
1120 | from->children = NULL((void*)0);
1121 | if (from->children_by_name) {
1122 | g_hash_table_destroy(from->children_by_name);
1123 | }
1124 | from->children_by_name = NULL((void*)0);
1125 |
1126 | if (from->nodetype == PBL_MESSAGE) {
1127 | pbl_message_descriptor_t* msg = (pbl_message_descriptor_t*) from;
1128 | if (msg->fields) {
1129 | g_queue_free(msg->fields);
1130 | msg->fields = NULL((void*)0);
1131 | }
1132 | if (msg->fields_by_number) {
1133 | g_hash_table_destroy(msg->fields_by_number);
1134 | msg->fields_by_number = NULL((void*)0);
1135 | }
1136 | } else if (from->nodetype == PBL_ENUM) {
1137 | pbl_enum_descriptor_t* anEnum = (pbl_enum_descriptor_t*) from;
1138 | if (anEnum->values) {
1139 | g_queue_free(anEnum->values);
1140 | anEnum->values = NULL((void*)0);
1141 | }
1142 | if (anEnum->values_by_number) {
1143 | g_hash_table_destroy(anEnum->values_by_number);
1144 | anEnum->values_by_number = NULL((void*)0);
1145 | }
1146 | }
1147 | }
1148 |
1149 | return to;
1150 | }
1151 |
1152 | /* free a pbl_node_t and its children. */
1153 | void
1154 | // NOLINTNEXTLINE(misc-no-recursion)
1155 | pbl_free_node(void *anode)
1156 | {
1157 | pbl_method_descriptor_t* method_node;
1158 | pbl_message_descriptor_t* message_node;
1159 | pbl_field_descriptor_t* field_node;
1160 | pbl_enum_descriptor_t* enum_node;
1161 | pbl_option_descriptor_t* option_node;
1162 | pbl_node_t* node = (pbl_node_t*) anode;
1163 |
1164 | if (node == NULL((void*)0)) return;
1165 |
1166 | switch (node->nodetype) {
1167 | case PBL_METHOD:
1168 | method_node = (pbl_method_descriptor_t*) node;
1169 | g_free(method_node->in_msg_type);
1170 | g_free(method_node->out_msg_type);
1171 | break;
1172 | case PBL_MESSAGE:
1173 | message_node = (pbl_message_descriptor_t*) node;
1174 | if (message_node->fields) {
1175 | g_queue_free(message_node->fields);
1176 | }
1177 | if (message_node->fields_by_number) {
1178 | g_hash_table_destroy(message_node->fields_by_number);
1179 | }
1180 | break;
1181 | case PBL_FIELD:
1182 | case PBL_MAP_FIELD:
1183 | field_node = (pbl_field_descriptor_t*) node;
1184 | g_free(field_node->type_name);
1185 | if (field_node->orig_default_value) {
1186 | g_free(field_node->orig_default_value);
1187 | }
1188 | if ((field_node->type == PROTOBUF_TYPE_STRING || field_node->type == PROTOBUF_TYPE_BYTES)
1189 | && field_node->default_value.s) {
1190 | g_free(field_node->default_value.s);
1191 | }
1192 | if (field_node->options_node) {
1193 | // We recurse here, but we're limited by depth checks at allocation time
1194 | pbl_free_node(field_node->options_node);
1195 | }
1196 | break;
1197 | case PBL_ENUM:
1198 | enum_node = (pbl_enum_descriptor_t*) node;
1199 | if (enum_node->values) {
1200 | g_queue_free(enum_node->values);
1201 | }
1202 | if (enum_node->values_by_number) {
1203 | g_hash_table_destroy(enum_node->values_by_number);
1204 | }
1205 | break;
1206 | case PBL_OPTION:
1207 | option_node = (pbl_option_descriptor_t*) node;
1208 | g_free(option_node->value);
1209 | break;
1210 | default:
1211 | /* do nothing */
1212 | break;
1213 | }
1214 |
1215 | g_free(node->name);
1216 | g_free(node->full_name);
1217 | if (node->children) {
1218 | g_queue_free_full(node->children, pbl_free_node);
1219 | }
1220 | if (node->children_by_name) {
1221 | g_hash_table_destroy(node->children_by_name);
1222 | }
1223 | g_free(node);
1224 | }
1225 |
1226 | /*
1227 | * Editor modelines - https://www.wireshark.org/tools/modelines.html
1228 | *
1229 | * Local variables:
1230 | * c-basic-offset: 4
1231 | * tab-width: 8
1232 | * indent-tabs-mode: nil
1233 | * End:
1234 | *
1235 | * vi: set shiftwidth=4 tabstop=8 expandtab:
1236 | * :indentSize=4:tabSize=8:noTabs=true:
1237 | */