File: | builds/wireshark/wireshark/epan/protobuf_lang_tree.c |
Warning: | line 1044, column 17 Potential leak of memory pointed to by 'node' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
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(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 */ | ||||
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 | 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 | |||||
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) { | ||||
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)); | ||||
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 | |||||
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
| ||||
| |||||
1016 | return parent; | ||||
1017 | } | ||||
1018 | |||||
1019 | if (!check_node_depth(parent)) { | ||||
1020 | return NULL((void*)0); | ||||
1021 | } | ||||
1022 | |||||
1023 | /* add a message node for mapField first */ | ||||
1024 | if (child->nodetype == PBL_MAP_FIELD) { | ||||
1025 | node = pbl_create_node(child->file, child->lineno, PBL_MESSAGE, ((pbl_field_descriptor_t*)child)->type_name); | ||||
1026 | pbl_merge_children(node, child); | ||||
1027 | pbl_add_child(parent, node); | ||||
1028 | } | ||||
1029 | |||||
1030 | child->parent = parent; | ||||
1031 | |||||
1032 | /* add child to children list */ | ||||
1033 | if (parent->children == NULL((void*)0)) { | ||||
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)) { | ||||
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 | ||||
| |||||
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 | */ |