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

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-18/lib/clang/18 -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/libxml2 -isystem /usr/include/lua5.4 -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-18/lib/clang/18/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-truncation -Wno-format-nonliteral -Wno-pointer-sign -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -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 -dwarf-debug-flags /usr/lib/llvm-18/bin/clang -### --analyze -x c -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 -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/libxml2 -isystem /usr/include/lua5.4 -fvisibility=hidden -fexcess-precision=fast -fstrict-flex-arrays=3 -fstack-clash-protection -fcf-protection=full -D _GLIBCXX_ASSERTIONS -fstack-protector-strong -fno-delete-null-pointer-checks -fno-strict-overflow -fno-strict-aliasing -fexceptions -Wno-format-truncation -Wno-format-nonliteral -fdiagnostics-color=always -Wno-pointer-sign -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -std=gnu11 -fPIC /builds/wireshark/wireshark/epan/protobuf_lang_tree.c -o /builds/wireshark/wireshark/sbout/2024-11-18-100252-3912-1 -Xclang -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2024-11-18-100252-3912-1 -x c /builds/wireshark/wireshark/epan/protobuf_lang_tree.c
1/* protobuf_lang_tree.c
2 *
3 * Routines of building and reading Protocol Buffers Language grammar tree.
4 * Copyright 2019, Huang Qiangxiong <[email protected]>
5 *
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <[email protected]>
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
22static bool_Bool
23check_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
35extern void
36pbl_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 */
50static char*
51protobuf_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 */
106void
107pbl_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 */
128void
129pbl_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 */
150static char*
151pbl_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 */
184bool_Bool
185pbl_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 */
237static pbl_node_t*
238pbl_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. */
294const char*
295// NOLINTNEXTLINE(misc-no-recursion)
296pbl_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 */
332static const pbl_node_t*
333pbl_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 */
390const pbl_method_descriptor_t*
391pbl_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() */
398const char*
399pbl_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() */
405const char*
406pbl_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() */
412const pbl_message_descriptor_t*
413pbl_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() */
420const pbl_message_descriptor_t*
421pbl_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() */
428const pbl_message_descriptor_t*
429pbl_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() */
436const char*
437pbl_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() */
443const char*
444pbl_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() */
450int
451pbl_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() */
457const pbl_field_descriptor_t*
458pbl_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() */
464const pbl_field_descriptor_t*
465pbl_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() */
475const pbl_field_descriptor_t*
476pbl_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() */
486const char*
487pbl_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() */
493const char*
494pbl_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() */
500int
501pbl_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() */
507int
508pbl_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() */
528int
529pbl_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() */
535int
536pbl_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() */
573const char*
574pbl_field_descriptor_TypeName(int field_type)
575{
576 return val_to_str(field_type, protobuf_field_type, "UNKNOWN_FIELD_TYPE(%d)");
577}
578
579/* like FieldDescriptor::message_type() type = TYPE_MESSAGE or TYPE_GROUP */
580const pbl_message_descriptor_t*
581pbl_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 */
592const pbl_enum_descriptor_t*
593pbl_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() */
604bool_Bool
605pbl_field_descriptor_is_required(const pbl_field_descriptor_t* field)
606{
607 return field->is_required;
608}
609
610/* like FieldDescriptor::has_default_value() */
611bool_Bool
612pbl_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() */
618int32_t
619pbl_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() */
625int64_t
626pbl_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() */
632uint32_t
633pbl_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() */
639uint64_t
640pbl_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() */
646float
647pbl_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() */
653double
654pbl_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() */
660bool_Bool
661pbl_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() */
667const char*
668pbl_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() */
675const pbl_enum_value_descriptor_t*
676pbl_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() */
696const char*
697pbl_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() */
703const char*
704pbl_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() */
710int
711pbl_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() */
717const pbl_enum_value_descriptor_t*
718pbl_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() */
724const pbl_enum_value_descriptor_t*
725pbl_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() */
735const pbl_enum_value_descriptor_t*
736pbl_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() */
746const char*
747pbl_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() */
753const char*
754pbl_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() */
760int
761pbl_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
766static void
767// NOLINTNEXTLINE(misc-no-recursion)
768pbl_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 */
790void
791pbl_foreach_message(const pbl_descriptor_pool_t* pool, void (*cb)(const pbl_message_descriptor_t*, void*), void* userdata)
792{
793 GHashTableIter it;
794 gpointer 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
806static void
807pbl_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 */
816pbl_node_t*
817pbl_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;
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
841pbl_node_t*
842pbl_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
852static pbl_option_descriptor_t*
853pbl_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 */
863pbl_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. */
880static int
881pbl_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 */
892pbl_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 */
971pbl_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 */
987pbl_node_t*
988pbl_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 */
998pbl_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 */
1010pbl_node_t*
1011// NOLINTNEXTLINE(misc-no-recursion)
1012pbl_add_child(pbl_node_t* parent, pbl_node_t* child)
1013{
1014 pbl_node_t* node = NULL((void*)0);
1015 if (child
11.1
'child' is not equal to NULL
== NULL((void*)0)
|| parent
11.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
12
Taking false branch
1016 return parent;
1017 }
1018
1019 if (!check_node_depth(parent)) {
4
Taking false branch
13
Assuming the condition is true
14
Taking true branch
1020 return NULL((void*)0);
15
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'
10
Returned allocated memory
1026 pbl_merge_children(node, child);
1027 pbl_add_child(parent, node);
11
Calling 'pbl_add_child'
16
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)) {
17
Assuming field 'children' is not equal to NULL
18
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)) {
19
Assuming field 'children_by_name' is not equal to NULL
20
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
21
Assuming 'node' is non-null
22
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 */
1102pbl_node_t*
1103// NOLINTNEXTLINE(misc-no-recursion)
1104pbl_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. */
1153void
1154// NOLINTNEXTLINE(misc-no-recursion)
1155pbl_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 */