/src/wireshark/epan/protobuf_lang_tree.c
Line | Count | Source |
1 | | /* protobuf_lang_tree.c |
2 | | * |
3 | | * Routines of building and reading Protocol Buffers Language grammar tree. |
4 | | * Copyright 2019, Huang Qiangxiong <qiangxiong.huang@qq.com> |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | */ |
12 | | |
13 | | #include <string.h> |
14 | | #include <stddef.h> |
15 | | #include <stdio.h> |
16 | | #include <stdlib.h> |
17 | | #include <limits.h> |
18 | | #include "protobuf_lang_tree.h" |
19 | | #include "protobuf-helper.h" /* only for PROTOBUF_TYPE_XXX enumeration */ |
20 | | |
21 | 0 | #define MAX_PROTOBUF_NODE_DEPTH 100 |
22 | | static bool |
23 | | check_node_depth(const pbl_node_t *node) |
24 | 0 | { |
25 | 0 | int depth = 1; |
26 | 0 | for (const pbl_node_t *parent = node; parent ; parent = parent->parent) { |
27 | 0 | depth++; |
28 | 0 | } |
29 | 0 | if (depth > MAX_PROTOBUF_NODE_DEPTH) { |
30 | 0 | return false; |
31 | 0 | } |
32 | 0 | return true; |
33 | 0 | } |
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 | 0 | { |
53 | 0 | int src_len; |
54 | 0 | uint8_t* dst, * q; |
55 | 0 | const char* p = src; |
56 | |
|
57 | 0 | if (!(src && size && (src_len = (int)strlen(src)))) |
58 | 0 | return NULL; |
59 | | |
60 | 0 | dst = q = (uint8_t *) g_malloc0(src_len + 1); |
61 | |
|
62 | 0 | while (p < src + src_len && *p) { |
63 | 0 | if (*p == '\\') { |
64 | 0 | p++; |
65 | |
|
66 | 0 | if (*p == 'x' || *p == 'X') { /* unescape hex byte */ |
67 | 0 | *q++ = (uint8_t)strtol(p + 1, (char**)&p, 16); |
68 | 0 | continue; |
69 | 0 | } |
70 | | |
71 | 0 | if (*p >= '0' && *p <= '7') { /* unescape octal byte */ |
72 | 0 | *q++ = (uint8_t)strtol(p, (char**)&p, 8); |
73 | 0 | continue; |
74 | 0 | } |
75 | | |
76 | 0 | switch (*p) |
77 | 0 | { |
78 | 0 | case 'a': *q++ = '\a'; break; |
79 | 0 | case 'b': *q++ = '\b'; break; |
80 | 0 | case 'f': *q++ = '\f'; break; |
81 | 0 | case 'n': *q++ = '\n'; break; |
82 | 0 | case 'r': *q++ = '\r'; break; |
83 | 0 | case 't': *q++ = '\t'; break; |
84 | 0 | case 'v': *q++ = '\v'; break; |
85 | 0 | default: /* include copying '\', "'" or '"' */ |
86 | 0 | *q++ = *p; |
87 | 0 | break; |
88 | 0 | } |
89 | 0 | } else { |
90 | 0 | *q++ = *p; |
91 | 0 | } |
92 | 0 | p++; |
93 | 0 | } |
94 | 0 | *q = 0; |
95 | 0 | *size = (int)(q - dst); |
96 | |
|
97 | 0 | return (char*) dst; |
98 | 0 | } |
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 | 0 | { |
109 | 0 | unsigned i; |
110 | |
|
111 | 0 | pbl_free_pool(*ppool); |
112 | 0 | pbl_descriptor_pool_t* p = g_new0(pbl_descriptor_pool_t, 1); |
113 | |
|
114 | 0 | p->source_paths = g_queue_new(); |
115 | 0 | for (i = 0; directories[i] != NULL; i++) { |
116 | 0 | g_queue_push_tail(p->source_paths, g_strdup(directories[i])); |
117 | 0 | } |
118 | |
|
119 | 0 | p->error_cb = error_cb ? error_cb : pbl_printf; |
120 | 0 | p->packages = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, pbl_free_node); |
121 | 0 | p->proto_files = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); |
122 | 0 | p->proto_files_to_be_parsed = g_queue_new(); |
123 | |
|
124 | 0 | *ppool = p; |
125 | 0 | } |
126 | | |
127 | | /* free all memory used by this protocol buffers language pool */ |
128 | | void |
129 | | pbl_free_pool(pbl_descriptor_pool_t* pool) |
130 | 0 | { |
131 | 0 | if (pool == NULL) return; |
132 | | |
133 | 0 | g_queue_free_full(pool->source_paths, g_free); |
134 | 0 | g_hash_table_destroy(pool->packages); |
135 | 0 | g_queue_free(pool->proto_files_to_be_parsed); /* elements will be removed in p->proto_files */ |
136 | 0 | g_hash_table_destroy(pool->proto_files); |
137 | |
|
138 | 0 | g_free(pool); |
139 | 0 | } |
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 | 0 | { |
153 | 0 | int i, j; |
154 | 0 | char* canon_path = g_new(char, strlen(path) + 1); |
155 | | /* replace all '\' to '/', and change '//' to '/' */ |
156 | 0 | for (i = 0, j = 0; path[i] != '\0'; i++) { |
157 | 0 | if (path[i] == '\\' || path[i] == '/') { |
158 | 0 | if (j > 0 && canon_path[j-1] == '/') { |
159 | | /* ignore redundant slash */ |
160 | 0 | } else { |
161 | 0 | canon_path[j++] = '/'; |
162 | 0 | } |
163 | 0 | } else { |
164 | | #ifdef _WIN32 |
165 | | canon_path[j++] = g_ascii_tolower(path[i]); |
166 | | #else |
167 | 0 | canon_path[j++] = path[i]; |
168 | 0 | #endif |
169 | 0 | } |
170 | 0 | } |
171 | 0 | canon_path[j] = '\0'; |
172 | |
|
173 | 0 | if (g_path_is_absolute(canon_path) |
174 | 0 | && g_file_test(canon_path, G_FILE_TEST_IS_REGULAR) |
175 | 0 | && strstr(canon_path, "/../") == NULL) { |
176 | 0 | return canon_path; |
177 | 0 | } else { |
178 | 0 | g_free(canon_path); |
179 | 0 | return NULL; |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | | /* Add a file into to do list */ |
184 | | bool |
185 | | pbl_add_proto_file_to_be_parsed(pbl_descriptor_pool_t* pool, const char* filepath) |
186 | 0 | { |
187 | 0 | char* path = NULL; |
188 | 0 | GList* it = NULL; |
189 | 0 | char* concat_path = NULL; |
190 | | |
191 | | /* Try to get the absolute path of the file */ |
192 | 0 | if (g_path_is_absolute(filepath)) { |
193 | 0 | path = pbl_canonicalize_absolute_filepath(filepath); |
194 | 0 | } |
195 | |
|
196 | 0 | if (path == NULL) { |
197 | | /* try to concat with source directories */ |
198 | 0 | for (it = g_queue_peek_head_link(pool->source_paths); it; it = it->next) { |
199 | 0 | concat_path = g_build_filename((char*)it->data, filepath, NULL); |
200 | 0 | path = pbl_canonicalize_absolute_filepath(concat_path); |
201 | 0 | g_free(concat_path); |
202 | 0 | if (path) break; |
203 | 0 | } |
204 | 0 | } |
205 | |
|
206 | 0 | if (path == NULL) { |
207 | 0 | if (pool->parser_state) { |
208 | | /* only happened during parsing an 'import' line of a .proto file */ |
209 | 0 | pbl_parser_error(pool->parser_state, "file [%s] does not exist!\n", filepath); |
210 | 0 | } else { |
211 | | /* normally happened during initializing a pool by adding files that need be loaded */ |
212 | 0 | pool->error_cb("Protobuf: file [%s] does not exist!\n", filepath); |
213 | 0 | } |
214 | 0 | return false; |
215 | 0 | } |
216 | | |
217 | 0 | if (!g_hash_table_lookup(pool->proto_files, path)) { |
218 | | /* create file descriptor info */ |
219 | 0 | pbl_file_descriptor_t* file = g_new0(pbl_file_descriptor_t, 1); |
220 | 0 | file->filename = path; |
221 | 0 | file->syntax_version = 2; |
222 | 0 | file->package_name = PBL_DEFAULT_PACKAGE_NAME; |
223 | 0 | file->package_name_lineno = -1; |
224 | 0 | file->pool = pool; |
225 | | |
226 | | /* store in hash table and list */ |
227 | 0 | g_hash_table_insert(pool->proto_files, path, file); |
228 | 0 | g_queue_push_tail(pool->proto_files_to_be_parsed, path); |
229 | 0 | } else { |
230 | | /* The file is already in the proto_files */ |
231 | 0 | g_free(path); |
232 | 0 | } |
233 | 0 | return true; |
234 | 0 | } |
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 | 0 | { |
240 | 0 | char* full_name_buf; |
241 | 0 | int len, i; |
242 | 0 | pbl_node_t* package; |
243 | 0 | pbl_node_t* node = NULL; |
244 | 0 | GSList* names = NULL; /* NULL terminated name retrieved from full_name */ |
245 | 0 | GSList* it = NULL; |
246 | |
|
247 | 0 | if (pool == NULL || full_name == NULL || pool->packages == NULL) { |
248 | 0 | return NULL; |
249 | 0 | } |
250 | | |
251 | 0 | if (full_name[0] == '.') { |
252 | 0 | full_name++; /* skip leading dot */ |
253 | 0 | } |
254 | |
|
255 | 0 | full_name_buf = g_strdup(full_name); |
256 | 0 | len = (int)strlen(full_name_buf); |
257 | | /* scan from end to begin, and replace '.' to '\0' */ |
258 | 0 | for (i = len-1; i >= 0; i--) { |
259 | 0 | if (full_name_buf[i] == '.' || i == 0) { |
260 | 0 | if (i == 0) { |
261 | | /* no dot any more, we search in default package */ |
262 | 0 | names = g_slist_prepend(names, full_name_buf); |
263 | 0 | package = (pbl_node_t*) g_hash_table_lookup(pool->packages, PBL_DEFAULT_PACKAGE_NAME); |
264 | 0 | } else { /* replace middle dot with '\0' */ |
265 | | /* push name at top of names */ |
266 | 0 | names = g_slist_prepend(names, full_name_buf + i + 1); |
267 | 0 | full_name_buf[i] = 0; |
268 | | /* take 0~i of full_name_buf as package name */ |
269 | 0 | package = (pbl_node_t*) g_hash_table_lookup(pool->packages, full_name_buf); |
270 | 0 | } |
271 | 0 | if (package) { |
272 | 0 | node = package; |
273 | | /* search node in this package */ |
274 | 0 | for (it = names; (it && node && node->children_by_name); it = it->next) { |
275 | 0 | node = (pbl_node_t*) g_hash_table_lookup(node->children_by_name, it->data); |
276 | 0 | } |
277 | |
|
278 | 0 | if (it == NULL && node && node->nodetype == nodetype) { |
279 | 0 | break; /* found */ |
280 | 0 | } |
281 | 0 | node = NULL; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | } |
285 | |
|
286 | 0 | if (names) { |
287 | 0 | g_slist_free(names); |
288 | 0 | } |
289 | 0 | g_free(full_name_buf); |
290 | 0 | return node; |
291 | 0 | } |
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 | 0 | { |
298 | 0 | const char* parent_full_name; |
299 | 0 | if (node == NULL |
300 | 0 | || node->nodetype == PBL_UNKNOWN |
301 | 0 | || node->nodetype == PBL_OPTIONS |
302 | 0 | || node->nodetype == PBL_OPTION) { |
303 | 0 | return NULL; |
304 | 0 | } |
305 | | |
306 | 0 | if (node->full_name) { |
307 | 0 | return node->full_name; |
308 | 0 | } |
309 | | |
310 | 0 | if (node->nodetype == PBL_ONEOF) { |
311 | 0 | if (!check_node_depth(node)) { |
312 | 0 | return NULL; |
313 | 0 | } |
314 | 0 | return pbl_get_node_full_name(node->parent); |
315 | 0 | } |
316 | | |
317 | 0 | if (node->nodetype == PBL_PACKAGE) { |
318 | 0 | node->full_name = g_strdup(node->name); |
319 | 0 | } else { |
320 | 0 | parent_full_name = pbl_get_node_full_name(node->parent); |
321 | 0 | if (parent_full_name && parent_full_name[0] != 0) { |
322 | 0 | node->full_name = g_strconcat(parent_full_name, ".", node->name, NULL); |
323 | 0 | } else { |
324 | 0 | node->full_name = g_strdup(node->name); |
325 | 0 | } |
326 | 0 | } |
327 | |
|
328 | 0 | return node->full_name; |
329 | 0 | } |
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 | 0 | { |
335 | 0 | const pbl_node_t* node = NULL; |
336 | 0 | pbl_descriptor_pool_t* pool = NULL; |
337 | 0 | char* parent_name; |
338 | 0 | char* full_name; |
339 | |
|
340 | 0 | if (context == NULL || name == NULL) { |
341 | 0 | return NULL; |
342 | 0 | } |
343 | | |
344 | 0 | if (name[0] == '.') { |
345 | | /* A leading '.' (for example, .foo.bar.Baz) means to start from the outermost scope. */ |
346 | 0 | if (context->file && context->file->pool) { |
347 | 0 | return pbl_find_node_in_pool(context->file->pool, name, nodetype); |
348 | 0 | } else { |
349 | 0 | return NULL; |
350 | 0 | } |
351 | 0 | } |
352 | | |
353 | | /* find pool */ |
354 | 0 | if (context->file) { |
355 | 0 | pool = context->file->pool; |
356 | 0 | } |
357 | | |
358 | | /* try find node in the context or parents (message or package) of the context */ |
359 | 0 | if (pool) { |
360 | 0 | int remaining; |
361 | 0 | parent_name = g_strdup(pbl_get_node_full_name((pbl_node_t*) context)); |
362 | 0 | remaining = (int)strlen(parent_name); |
363 | 0 | while (remaining > 0) { |
364 | 0 | full_name = g_strconcat(parent_name, ".", name, NULL); |
365 | 0 | node = pbl_find_node_in_pool(pool, full_name, nodetype); |
366 | 0 | g_free(full_name); |
367 | 0 | if (node) { |
368 | 0 | g_free(parent_name); |
369 | 0 | return node; |
370 | 0 | } |
371 | | /* scan from end to begin, and replace first '.' to '\0' */ |
372 | 0 | for (remaining--; remaining > 0; remaining--) { |
373 | 0 | if (parent_name[remaining] == '.') { |
374 | | /* found a potential parent node name */ |
375 | 0 | parent_name[remaining] = '\0'; |
376 | 0 | break; /* break from the 'for' loop, continue 'while' loop */ |
377 | 0 | } |
378 | 0 | } |
379 | 0 | } |
380 | 0 | g_free(parent_name); |
381 | | |
382 | | /* try find node in pool directly */ |
383 | 0 | return pbl_find_node_in_pool(pool, name, nodetype); |
384 | 0 | } |
385 | | |
386 | 0 | return NULL; |
387 | 0 | } |
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 | 0 | { |
393 | 0 | pbl_node_t* n = pbl_find_node_in_pool(pool, full_name, PBL_METHOD); |
394 | 0 | return n ? (pbl_method_descriptor_t*)n : NULL; |
395 | 0 | } |
396 | | |
397 | | /* like MethodDescriptor::name() */ |
398 | | const char* |
399 | | pbl_method_descriptor_name(const pbl_method_descriptor_t* method) |
400 | 0 | { |
401 | 0 | return pbl_get_node_name((pbl_node_t*)method); |
402 | 0 | } |
403 | | |
404 | | /* like MethodDescriptor::full_name() */ |
405 | | const char* |
406 | | pbl_method_descriptor_full_name(const pbl_method_descriptor_t* method) |
407 | 0 | { |
408 | 0 | return pbl_get_node_full_name((pbl_node_t*)method); |
409 | 0 | } |
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 | 0 | { |
415 | 0 | const pbl_node_t* n = pbl_find_node_in_context((pbl_node_t*)method, method->in_msg_type, PBL_MESSAGE); |
416 | 0 | return n ? (const pbl_message_descriptor_t*)n : NULL; |
417 | 0 | } |
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 | 0 | { |
423 | 0 | const pbl_node_t* n = pbl_find_node_in_context((pbl_node_t*)method, method->out_msg_type, PBL_MESSAGE); |
424 | 0 | return n ? (const pbl_message_descriptor_t*)n : NULL; |
425 | 0 | } |
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 | 0 | { |
431 | 0 | pbl_node_t* n = pbl_find_node_in_pool(pool, name, PBL_MESSAGE); |
432 | 0 | return n ? (pbl_message_descriptor_t*)n : NULL; |
433 | 0 | } |
434 | | |
435 | | /* like Descriptor::name() */ |
436 | | const char* |
437 | | pbl_message_descriptor_name(const pbl_message_descriptor_t* message) |
438 | 0 | { |
439 | 0 | return pbl_get_node_name((pbl_node_t*)message); |
440 | 0 | } |
441 | | |
442 | | /* like Descriptor::full_name() */ |
443 | | const char* |
444 | | pbl_message_descriptor_full_name(const pbl_message_descriptor_t* message) |
445 | 0 | { |
446 | 0 | return pbl_get_node_full_name((pbl_node_t*)message); |
447 | 0 | } |
448 | | |
449 | | /* like Descriptor::field_count() */ |
450 | | int |
451 | | pbl_message_descriptor_field_count(const pbl_message_descriptor_t* message) |
452 | 0 | { |
453 | 0 | return (message && message->fields) ? g_queue_get_length(message->fields) : 0; |
454 | 0 | } |
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 | 0 | { |
460 | 0 | return (message && message->fields) ? (pbl_field_descriptor_t*) g_queue_peek_nth(message->fields, field_index) : NULL; |
461 | 0 | } |
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 | 0 | { |
467 | 0 | if (message && message->fields_by_number) { |
468 | 0 | return (pbl_field_descriptor_t*) g_hash_table_lookup(message->fields_by_number, GINT_TO_POINTER(number)); |
469 | 0 | } else { |
470 | 0 | return NULL; |
471 | 0 | } |
472 | 0 | } |
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 | 0 | { |
478 | 0 | if (message && ((pbl_node_t*)message)->children_by_name) { |
479 | 0 | return (pbl_field_descriptor_t*) g_hash_table_lookup(((pbl_node_t*)message)->children_by_name, name); |
480 | 0 | } else { |
481 | 0 | return NULL; |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | | /* like FieldDescriptor::full_name() */ |
486 | | const char* |
487 | | pbl_field_descriptor_full_name(const pbl_field_descriptor_t* field) |
488 | 0 | { |
489 | 0 | return pbl_get_node_full_name((pbl_node_t*)field); |
490 | 0 | } |
491 | | |
492 | | /* like FieldDescriptor::name() */ |
493 | | const char* |
494 | | pbl_field_descriptor_name(const pbl_field_descriptor_t* field) |
495 | 0 | { |
496 | 0 | return pbl_get_node_name((pbl_node_t*)field); |
497 | 0 | } |
498 | | |
499 | | /* like FieldDescriptor::number() */ |
500 | | int |
501 | | pbl_field_descriptor_number(const pbl_field_descriptor_t* field) |
502 | 0 | { |
503 | 0 | return GPOINTER_TO_INT(field->number); |
504 | 0 | } |
505 | | |
506 | | /* like FieldDescriptor::type() */ |
507 | | int |
508 | | pbl_field_descriptor_type(const pbl_field_descriptor_t* field) |
509 | 0 | { |
510 | 0 | const pbl_node_t* node; |
511 | 0 | if (field->type == PROTOBUF_TYPE_NONE) { |
512 | | /* try to lookup as ENUM */ |
513 | 0 | node = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_ENUM); |
514 | 0 | if (node) { |
515 | 0 | ((pbl_field_descriptor_t*)field)->type = PROTOBUF_TYPE_ENUM; |
516 | 0 | } else { |
517 | | /* try to lookup as MESSAGE */ |
518 | 0 | node = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_MESSAGE); |
519 | 0 | if (node) { |
520 | 0 | ((pbl_field_descriptor_t*)field)->type = PROTOBUF_TYPE_MESSAGE; |
521 | 0 | } |
522 | 0 | } |
523 | 0 | } |
524 | 0 | return field->type; |
525 | 0 | } |
526 | | |
527 | | /* like FieldDescriptor::is_repeated() */ |
528 | | int |
529 | | pbl_field_descriptor_is_repeated(const pbl_field_descriptor_t* field) |
530 | 0 | { |
531 | 0 | return field->is_repeated ? 1 : 0; |
532 | 0 | } |
533 | | |
534 | | /* like FieldDescriptor::is_packed() */ |
535 | | int |
536 | | pbl_field_descriptor_is_packed(const pbl_field_descriptor_t* field) |
537 | 0 | { |
538 | 0 | bool has_packed_option; |
539 | 0 | bool packed_option_value; |
540 | 0 | int syntax_version = ((pbl_node_t*)field)->file->syntax_version; |
541 | | |
542 | | /* determine packed flag */ |
543 | 0 | if (field->is_repeated == false) { |
544 | 0 | return false; |
545 | 0 | } |
546 | | /* note: field->type may be undetermined until calling pbl_field_descriptor_type() */ |
547 | 0 | switch (pbl_field_descriptor_type(field)) { |
548 | 0 | case PROTOBUF_TYPE_STRING: |
549 | 0 | case PROTOBUF_TYPE_GROUP: |
550 | 0 | case PROTOBUF_TYPE_MESSAGE: |
551 | 0 | case PROTOBUF_TYPE_BYTES: |
552 | 0 | return false; |
553 | 0 | default: /* only repeated fields of primitive numeric types can be declared "packed". */ |
554 | 0 | has_packed_option = field->options_node |
555 | 0 | && field->options_node->children_by_name |
556 | 0 | && g_hash_table_lookup(field->options_node->children_by_name, "packed"); |
557 | |
|
558 | 0 | packed_option_value = (has_packed_option ? |
559 | 0 | g_strcmp0( |
560 | 0 | ((pbl_option_descriptor_t*)g_hash_table_lookup( |
561 | 0 | field->options_node->children_by_name, "packed"))->value, "true") == 0 |
562 | 0 | : false); |
563 | |
|
564 | 0 | if (syntax_version == 2) { |
565 | 0 | return packed_option_value; |
566 | 0 | } else { /* packed default in syntax_version = 3 */ |
567 | 0 | return has_packed_option ? packed_option_value : true; |
568 | 0 | } |
569 | 0 | } |
570 | 0 | } |
571 | | |
572 | | /* like FieldDescriptor::TypeName() */ |
573 | | const char* |
574 | | pbl_field_descriptor_TypeName(wmem_allocator_t* scope, int field_type) |
575 | 0 | { |
576 | 0 | return val_to_str(scope, field_type, protobuf_field_type, "UNKNOWN_FIELD_TYPE(%d)"); |
577 | 0 | } |
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 | 0 | { |
583 | 0 | const pbl_node_t* n; |
584 | 0 | if (field->type == PROTOBUF_TYPE_MESSAGE || field->type == PROTOBUF_TYPE_GROUP) { |
585 | 0 | n = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_MESSAGE); |
586 | 0 | return n ? (const pbl_message_descriptor_t*)n : NULL; |
587 | 0 | } |
588 | 0 | return NULL; |
589 | 0 | } |
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 | 0 | { |
595 | 0 | const pbl_node_t* n; |
596 | 0 | if (field->type == PROTOBUF_TYPE_ENUM) { |
597 | 0 | n = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_ENUM); |
598 | 0 | return n ? (const pbl_enum_descriptor_t*)n : NULL; |
599 | 0 | } |
600 | 0 | return NULL; |
601 | 0 | } |
602 | | |
603 | | /* like FieldDescriptor::is_required() */ |
604 | | bool |
605 | | pbl_field_descriptor_is_required(const pbl_field_descriptor_t* field) |
606 | 0 | { |
607 | 0 | return field->is_required; |
608 | 0 | } |
609 | | |
610 | | /* like FieldDescriptor::has_default_value() */ |
611 | | bool |
612 | | pbl_field_descriptor_has_default_value(const pbl_field_descriptor_t* field) |
613 | 0 | { |
614 | 0 | return field->has_default_value; |
615 | 0 | } |
616 | | |
617 | | /* like FieldDescriptor::default_value_int32() */ |
618 | | int32_t |
619 | | pbl_field_descriptor_default_value_int32(const pbl_field_descriptor_t* field) |
620 | 0 | { |
621 | 0 | return field->default_value.i32; |
622 | 0 | } |
623 | | |
624 | | /* like FieldDescriptor::default_value_int64() */ |
625 | | int64_t |
626 | | pbl_field_descriptor_default_value_int64(const pbl_field_descriptor_t* field) |
627 | 0 | { |
628 | 0 | return field->default_value.i64; |
629 | 0 | } |
630 | | |
631 | | /* like FieldDescriptor::default_value_uint32() */ |
632 | | uint32_t |
633 | | pbl_field_descriptor_default_value_uint32(const pbl_field_descriptor_t* field) |
634 | 0 | { |
635 | 0 | return field->default_value.u32; |
636 | 0 | } |
637 | | |
638 | | /* like FieldDescriptor::default_value_uint64() */ |
639 | | uint64_t |
640 | | pbl_field_descriptor_default_value_uint64(const pbl_field_descriptor_t* field) |
641 | 0 | { |
642 | 0 | return field->default_value.u64; |
643 | 0 | } |
644 | | |
645 | | /* like FieldDescriptor::default_value_float() */ |
646 | | float |
647 | | pbl_field_descriptor_default_value_float(const pbl_field_descriptor_t* field) |
648 | 0 | { |
649 | 0 | return field->default_value.f; |
650 | 0 | } |
651 | | |
652 | | /* like FieldDescriptor::default_value_double() */ |
653 | | double |
654 | | pbl_field_descriptor_default_value_double(const pbl_field_descriptor_t* field) |
655 | 0 | { |
656 | 0 | return field->default_value.d; |
657 | 0 | } |
658 | | |
659 | | /* like FieldDescriptor::default_value_bool() */ |
660 | | bool |
661 | | pbl_field_descriptor_default_value_bool(const pbl_field_descriptor_t* field) |
662 | 0 | { |
663 | 0 | return field->default_value.b; |
664 | 0 | } |
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 | 0 | { |
670 | 0 | *size = field->string_or_bytes_default_value_length; |
671 | 0 | return field->default_value.s; |
672 | 0 | } |
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 | 0 | { |
678 | 0 | const pbl_enum_descriptor_t* enum_desc; |
679 | |
|
680 | 0 | if (pbl_field_descriptor_type(field) == PROTOBUF_TYPE_ENUM |
681 | 0 | && field->default_value.e == NULL && (enum_desc = pbl_field_descriptor_enum_type(field))) { |
682 | |
|
683 | 0 | if (field->orig_default_value) { |
684 | | /* find enum_value according to the name of default value */ |
685 | 0 | ((pbl_field_descriptor_t*)field)->default_value.e = pbl_enum_descriptor_FindValueByName(enum_desc, field->orig_default_value); |
686 | 0 | } else { |
687 | | /* the default value is the first defined enum value */ |
688 | 0 | ((pbl_field_descriptor_t*)field)->default_value.e = pbl_enum_descriptor_value(enum_desc, 0); |
689 | 0 | } |
690 | 0 | } |
691 | |
|
692 | 0 | return field->default_value.e; |
693 | 0 | } |
694 | | |
695 | | /* like EnumDescriptor::name() */ |
696 | | const char* |
697 | | pbl_enum_descriptor_name(const pbl_enum_descriptor_t* anEnum) |
698 | 0 | { |
699 | 0 | return pbl_get_node_name((pbl_node_t*)anEnum); |
700 | 0 | } |
701 | | |
702 | | /* like EnumDescriptor::full_name() */ |
703 | | const char* |
704 | | pbl_enum_descriptor_full_name(const pbl_enum_descriptor_t* anEnum) |
705 | 0 | { |
706 | 0 | return pbl_get_node_full_name((pbl_node_t*)anEnum); |
707 | 0 | } |
708 | | |
709 | | /* like EnumDescriptor::value_count() */ |
710 | | int |
711 | | pbl_enum_descriptor_value_count(const pbl_enum_descriptor_t* anEnum) |
712 | 0 | { |
713 | 0 | return (anEnum && anEnum->values) ? g_queue_get_length(anEnum->values) : 0; |
714 | 0 | } |
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 | 0 | { |
720 | 0 | return (anEnum && anEnum->values) ? (pbl_enum_value_descriptor_t*) g_queue_peek_nth(anEnum->values, value_index) : NULL; |
721 | 0 | } |
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 | 0 | { |
727 | 0 | if (anEnum && anEnum->values_by_number) { |
728 | 0 | return (pbl_enum_value_descriptor_t*) g_hash_table_lookup(anEnum->values_by_number, GINT_TO_POINTER(number)); |
729 | 0 | } else { |
730 | 0 | return NULL; |
731 | 0 | } |
732 | 0 | } |
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 | 0 | { |
738 | 0 | if (anEnum && ((pbl_node_t*)anEnum)->children_by_name) { |
739 | 0 | return (pbl_enum_value_descriptor_t*)g_hash_table_lookup(((pbl_node_t*)anEnum)->children_by_name, name); |
740 | 0 | } else { |
741 | 0 | return NULL; |
742 | 0 | } |
743 | 0 | } |
744 | | |
745 | | /* like EnumValueDescriptor::name() */ |
746 | | const char* |
747 | | pbl_enum_value_descriptor_name(const pbl_enum_value_descriptor_t* enumValue) |
748 | 0 | { |
749 | 0 | return pbl_get_node_name((pbl_node_t*)enumValue); |
750 | 0 | } |
751 | | |
752 | | /* like EnumValueDescriptor::full_name() */ |
753 | | const char* |
754 | | pbl_enum_value_descriptor_full_name(const pbl_enum_value_descriptor_t* enumValue) |
755 | 0 | { |
756 | 0 | return pbl_get_node_full_name((pbl_node_t*)enumValue); |
757 | 0 | } |
758 | | |
759 | | /* like EnumValueDescriptor::number() */ |
760 | | int |
761 | | pbl_enum_value_descriptor_number(const pbl_enum_value_descriptor_t* enumValue) |
762 | 0 | { |
763 | 0 | return GPOINTER_TO_INT(enumValue->number); |
764 | 0 | } |
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 | 0 | { |
770 | 0 | GList* it; |
771 | 0 | if (node == NULL) { |
772 | 0 | return; |
773 | 0 | } |
774 | | |
775 | 0 | if (node->nodetype == PBL_MESSAGE) { |
776 | 0 | (*cb)((const pbl_message_descriptor_t*) node, userdata); |
777 | 0 | } |
778 | |
|
779 | 0 | if (node->children) { |
780 | 0 | if (!check_node_depth(node)) { |
781 | 0 | return; |
782 | 0 | } |
783 | 0 | for (it = g_queue_peek_head_link(node->children); it; it = it->next) { |
784 | 0 | pbl_traverse_sub_tree((const pbl_node_t*) it->data, cb, userdata); |
785 | 0 | } |
786 | 0 | } |
787 | 0 | } |
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 | 0 | { |
793 | 0 | GHashTableIter it; |
794 | 0 | void *key, *value; |
795 | 0 | g_hash_table_iter_init (&it, pool->packages); |
796 | 0 | while (g_hash_table_iter_next (&it, &key, &value)) { |
797 | 0 | pbl_traverse_sub_tree((const pbl_node_t*)value, cb, userdata); |
798 | 0 | } |
799 | 0 | } |
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 | 0 | { |
809 | 0 | node->nodetype = nodetype; |
810 | 0 | node->name = g_strdup(name); |
811 | 0 | node->file = file; |
812 | 0 | node->lineno = (lineno > -1) ? lineno : -1; |
813 | 0 | } |
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 | 0 | { |
819 | 0 | pbl_node_t* node = NULL; |
820 | |
|
821 | 0 | switch (nodetype) { |
822 | 0 | case PBL_METHOD: /* should use pbl_create_method_node() */ |
823 | 0 | case PBL_FIELD: /* should use pbl_create_field_node() */ |
824 | 0 | case PBL_MAP_FIELD: /* should use pbl_create_map_field_node() */ |
825 | 0 | case PBL_ENUM_VALUE: /* should use pbl_create_enum_value_node() */ |
826 | 0 | case PBL_OPTION: /* should use pbl_create_option_node() */ |
827 | 0 | return NULL; |
828 | 0 | case PBL_MESSAGE: |
829 | 0 | node = (pbl_node_t*) g_malloc0(sizeof(pbl_message_descriptor_t)); |
830 | 0 | break; |
831 | 0 | case PBL_ENUM: |
832 | 0 | node = (pbl_node_t*) g_malloc0(sizeof(pbl_enum_descriptor_t)); |
833 | 0 | break; |
834 | 0 | default: |
835 | 0 | node = g_new0(pbl_node_t, 1); |
836 | 0 | } |
837 | 0 | pbl_init_node(node, file, lineno, nodetype, name); |
838 | 0 | return node; |
839 | 0 | } |
840 | | |
841 | | pbl_node_t* |
842 | | pbl_set_node_name(pbl_node_t* node, int lineno, const char* newname) |
843 | 0 | { |
844 | 0 | g_free(node->name); |
845 | 0 | node->name = g_strdup(newname); |
846 | 0 | if (lineno > -1) { |
847 | 0 | node->lineno = lineno; |
848 | 0 | } |
849 | 0 | return node; |
850 | 0 | } |
851 | | |
852 | | static pbl_option_descriptor_t* |
853 | | pbl_get_option_by_name(pbl_node_t* options, const char* name) |
854 | 0 | { |
855 | 0 | if (options && options->children_by_name) { |
856 | 0 | return (pbl_option_descriptor_t*)g_hash_table_lookup(options->children_by_name, name); |
857 | 0 | } else { |
858 | 0 | return NULL; |
859 | 0 | } |
860 | 0 | } |
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 in_is_stream, const char* out_msg_type, bool out_is_stream) |
866 | 0 | { |
867 | 0 | pbl_method_descriptor_t* node = g_new0(pbl_method_descriptor_t, 1); |
868 | 0 | pbl_init_node(&node->basic_info, file, lineno, PBL_METHOD, name); |
869 | |
|
870 | 0 | node->in_msg_type = g_strdup(in_msg_type); |
871 | 0 | node->in_is_stream = in_is_stream; |
872 | 0 | node->out_msg_type = g_strdup(out_msg_type); |
873 | 0 | node->out_is_stream = out_is_stream; |
874 | |
|
875 | 0 | return (pbl_node_t*)node; |
876 | 0 | } |
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 | 0 | { |
883 | 0 | int i = str_to_val(type_name, protobuf_field_type, 0); |
884 | 0 | if (i == PROTOBUF_TYPE_GROUP || i == PROTOBUF_TYPE_MESSAGE || i == PROTOBUF_TYPE_ENUM) { |
885 | 0 | i = PROTOBUF_TYPE_NONE; /* complex type will find after parsing */ |
886 | 0 | } |
887 | |
|
888 | 0 | return i; |
889 | 0 | } |
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 | 0 | { |
895 | 0 | pbl_option_descriptor_t* default_option; |
896 | 0 | pbl_field_descriptor_t* node = g_new0(pbl_field_descriptor_t, 1); |
897 | 0 | pbl_init_node(&node->basic_info, file, lineno, PBL_FIELD, name); |
898 | |
|
899 | 0 | node->number = number; |
900 | 0 | node->options_node = options; |
901 | 0 | node->is_repeated = (g_strcmp0(label, "repeated") == 0); |
902 | 0 | node->type_name = g_strdup(type_name); |
903 | | /* type 0 means undetermined, it will be determined on |
904 | | calling pbl_field_descriptor_type() later */ |
905 | 0 | node->type = pbl_get_simple_type_enum_value_by_typename(type_name); |
906 | 0 | 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 | 0 | default_option = pbl_get_option_by_name(options, "default"); |
913 | 0 | if (default_option && default_option->value) { |
914 | 0 | node->has_default_value = true; |
915 | 0 | node->orig_default_value = g_strdup(default_option->value); |
916 | | /* get default value for simple type */ |
917 | 0 | switch (node->type) |
918 | 0 | { |
919 | 0 | case PROTOBUF_TYPE_INT32: |
920 | 0 | case PROTOBUF_TYPE_SINT32: |
921 | 0 | case PROTOBUF_TYPE_SFIXED32: |
922 | 0 | sscanf(node->orig_default_value, "%" SCNd32, &node->default_value.i32); |
923 | 0 | break; |
924 | | |
925 | 0 | case PROTOBUF_TYPE_INT64: |
926 | 0 | case PROTOBUF_TYPE_SINT64: |
927 | 0 | case PROTOBUF_TYPE_SFIXED64: |
928 | 0 | node->default_value.i64 = g_ascii_strtoll(node->orig_default_value, NULL, 10); |
929 | 0 | break; |
930 | | |
931 | 0 | case PROTOBUF_TYPE_UINT32: |
932 | 0 | case PROTOBUF_TYPE_FIXED32: |
933 | 0 | sscanf(node->orig_default_value, "%" SCNu32, &node->default_value.u32); |
934 | 0 | break; |
935 | | |
936 | 0 | case PROTOBUF_TYPE_UINT64: |
937 | 0 | case PROTOBUF_TYPE_FIXED64: |
938 | 0 | node->default_value.u64 = g_ascii_strtoull(node->orig_default_value, NULL, 10); |
939 | 0 | break; |
940 | | |
941 | 0 | case PROTOBUF_TYPE_BOOL: |
942 | 0 | node->default_value.b = (g_strcmp0(node->orig_default_value, "true") == 0); |
943 | 0 | break; |
944 | | |
945 | 0 | case PROTOBUF_TYPE_DOUBLE: |
946 | 0 | node->default_value.d = g_ascii_strtod(node->orig_default_value, NULL); |
947 | 0 | break; |
948 | | |
949 | 0 | case PROTOBUF_TYPE_FLOAT: |
950 | 0 | node->default_value.f = (float) g_ascii_strtod(node->orig_default_value, NULL); |
951 | 0 | break; |
952 | | |
953 | 0 | case PROTOBUF_TYPE_STRING: |
954 | 0 | case PROTOBUF_TYPE_BYTES: |
955 | 0 | node->default_value.s = protobuf_string_unescape(node->orig_default_value, &node->string_or_bytes_default_value_length); |
956 | 0 | break; |
957 | | |
958 | 0 | 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 | 0 | break; |
964 | 0 | } |
965 | 0 | } |
966 | | |
967 | 0 | return (pbl_node_t*)node; |
968 | 0 | } |
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 | 0 | { |
974 | 0 | pbl_field_descriptor_t* node = g_new0(pbl_field_descriptor_t, 1); |
975 | 0 | pbl_init_node(&node->basic_info, file, lineno, PBL_MAP_FIELD, name); |
976 | |
|
977 | 0 | node->number = number; |
978 | 0 | node->type_name = g_strconcat(name, "MapEntry", NULL); |
979 | 0 | node->type = PROTOBUF_TYPE_MESSAGE; |
980 | 0 | node->is_repeated = true; |
981 | 0 | node->options_node = options; |
982 | |
|
983 | 0 | return (pbl_node_t*)node; |
984 | 0 | } |
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 | 0 | { |
990 | 0 | pbl_enum_value_descriptor_t* node = g_new0(pbl_enum_value_descriptor_t, 1); |
991 | 0 | pbl_init_node(&node->basic_info, file, lineno, PBL_ENUM_VALUE, name); |
992 | |
|
993 | 0 | node->number = number; |
994 | 0 | return (pbl_node_t*)node; |
995 | 0 | } |
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 | 0 | { |
1001 | 0 | pbl_option_descriptor_t* node = g_new0(pbl_option_descriptor_t, 1); |
1002 | 0 | pbl_init_node(&node->basic_info, file, lineno, PBL_OPTION, name); |
1003 | |
|
1004 | 0 | if (value) |
1005 | 0 | node->value = g_strdup(value); |
1006 | 0 | return (pbl_node_t*)node; |
1007 | 0 | } |
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 | 0 | { |
1014 | 0 | pbl_node_t* node = NULL; |
1015 | 0 | if (child == NULL || parent == NULL) { |
1016 | 0 | return parent; |
1017 | 0 | } |
1018 | | |
1019 | 0 | if (!check_node_depth(parent)) { |
1020 | 0 | return NULL; |
1021 | 0 | } |
1022 | | |
1023 | | /* add a message node for mapField first */ |
1024 | 0 | if (child->nodetype == PBL_MAP_FIELD) { |
1025 | 0 | node = pbl_create_node(child->file, child->lineno, PBL_MESSAGE, ((pbl_field_descriptor_t*)child)->type_name); |
1026 | 0 | pbl_merge_children(node, child); |
1027 | 0 | pbl_add_child(parent, node); |
1028 | 0 | } |
1029 | |
|
1030 | 0 | child->parent = parent; |
1031 | | |
1032 | | /* add child to children list */ |
1033 | 0 | if (parent->children == NULL) { |
1034 | 0 | parent->children = g_queue_new(); |
1035 | 0 | } |
1036 | 0 | g_queue_push_tail(parent->children, child); |
1037 | | |
1038 | | /* add child to children_by_name table */ |
1039 | 0 | if (parent->children_by_name == NULL) { |
1040 | 0 | parent->children_by_name = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); |
1041 | 0 | } |
1042 | |
|
1043 | 0 | node = (pbl_node_t*) g_hash_table_lookup(parent->children_by_name, child->name); |
1044 | 0 | if (node && node->nodetype == PBL_OPTION && child->nodetype == PBL_OPTION |
1045 | 0 | && ((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 | 0 | char* oval = ((pbl_option_descriptor_t*)node)->value; |
1051 | 0 | char* nval = ((pbl_option_descriptor_t*)child)->value; |
1052 | 0 | ((pbl_option_descriptor_t*)child)->value = g_strconcat(oval, ",", nval, NULL); |
1053 | 0 | g_free(nval); |
1054 | 0 | } else if (node && child->file && parent->file |
1055 | 0 | && child->file->pool && child->file->pool->error_cb |
1056 | | /* Let's assume that any set of base types we point at are valid.. */ |
1057 | 0 | && !strstr(node->file->filename, "google")) { |
1058 | 0 | child->file->pool->error_cb( |
1059 | 0 | "Protobuf: Warning: \"%s\" of [%s:%d] is already defined in file [%s:%d].\n", |
1060 | 0 | child->name, child->file->filename, child->lineno, node->file->filename, node->lineno); |
1061 | 0 | } |
1062 | |
|
1063 | 0 | g_hash_table_insert(parent->children_by_name, child->name, child); |
1064 | |
|
1065 | 0 | if (parent->nodetype == PBL_MESSAGE) { |
1066 | 0 | pbl_message_descriptor_t* msg = (pbl_message_descriptor_t*) parent; |
1067 | | |
1068 | | /* add child to fields_by_number table */ |
1069 | 0 | if (child->nodetype == PBL_FIELD || child->nodetype == PBL_MAP_FIELD) { |
1070 | 0 | if (msg->fields == NULL) { |
1071 | 0 | msg->fields = g_queue_new(); |
1072 | 0 | } |
1073 | 0 | g_queue_push_tail(msg->fields, child); |
1074 | |
|
1075 | 0 | if (msg->fields_by_number == NULL) { |
1076 | 0 | msg->fields_by_number = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); |
1077 | 0 | } |
1078 | 0 | g_hash_table_insert(msg->fields_by_number, |
1079 | 0 | GINT_TO_POINTER(((pbl_field_descriptor_t*)child)->number), child); |
1080 | 0 | } |
1081 | |
|
1082 | 0 | } else if (parent->nodetype == PBL_ENUM && child->nodetype == PBL_ENUM_VALUE) { |
1083 | 0 | pbl_enum_descriptor_t* anEnum = (pbl_enum_descriptor_t*) parent; |
1084 | |
|
1085 | 0 | if (anEnum->values == NULL) { |
1086 | 0 | anEnum->values = g_queue_new(); |
1087 | 0 | } |
1088 | 0 | g_queue_push_tail(anEnum->values, child); |
1089 | | |
1090 | | /* add child to values_by_number table */ |
1091 | 0 | if (anEnum->values_by_number == NULL) { |
1092 | 0 | anEnum->values_by_number = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); |
1093 | 0 | } |
1094 | 0 | g_hash_table_insert(anEnum->values_by_number, |
1095 | 0 | GINT_TO_POINTER(((pbl_enum_value_descriptor_t*)child)->number), child); |
1096 | 0 | } |
1097 | |
|
1098 | 0 | return parent; |
1099 | 0 | } |
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 | 0 | { |
1106 | 0 | GList* it; |
1107 | 0 | pbl_node_t* child; |
1108 | |
|
1109 | 0 | if (to == NULL || from == NULL) { |
1110 | 0 | return to; |
1111 | 0 | } |
1112 | | |
1113 | 0 | if (from->children) { |
1114 | 0 | for (it = g_queue_peek_head_link(from->children); it; it = it->next) { |
1115 | 0 | child = (pbl_node_t*)it->data; |
1116 | 0 | pbl_add_child(to, child); |
1117 | 0 | } |
1118 | |
|
1119 | 0 | g_queue_free(from->children); |
1120 | 0 | from->children = NULL; |
1121 | 0 | if (from->children_by_name) { |
1122 | 0 | g_hash_table_destroy(from->children_by_name); |
1123 | 0 | } |
1124 | 0 | from->children_by_name = NULL; |
1125 | |
|
1126 | 0 | if (from->nodetype == PBL_MESSAGE) { |
1127 | 0 | pbl_message_descriptor_t* msg = (pbl_message_descriptor_t*) from; |
1128 | 0 | if (msg->fields) { |
1129 | 0 | g_queue_free(msg->fields); |
1130 | 0 | msg->fields = NULL; |
1131 | 0 | } |
1132 | 0 | if (msg->fields_by_number) { |
1133 | 0 | g_hash_table_destroy(msg->fields_by_number); |
1134 | 0 | msg->fields_by_number = NULL; |
1135 | 0 | } |
1136 | 0 | } else if (from->nodetype == PBL_ENUM) { |
1137 | 0 | pbl_enum_descriptor_t* anEnum = (pbl_enum_descriptor_t*) from; |
1138 | 0 | if (anEnum->values) { |
1139 | 0 | g_queue_free(anEnum->values); |
1140 | 0 | anEnum->values = NULL; |
1141 | 0 | } |
1142 | 0 | if (anEnum->values_by_number) { |
1143 | 0 | g_hash_table_destroy(anEnum->values_by_number); |
1144 | 0 | anEnum->values_by_number = NULL; |
1145 | 0 | } |
1146 | 0 | } |
1147 | 0 | } |
1148 | |
|
1149 | 0 | return to; |
1150 | 0 | } |
1151 | | |
1152 | | /* free a pbl_node_t and its children. */ |
1153 | | void |
1154 | | // NOLINTNEXTLINE(misc-no-recursion) |
1155 | | pbl_free_node(void *anode) |
1156 | 0 | { |
1157 | 0 | pbl_method_descriptor_t* method_node; |
1158 | 0 | pbl_message_descriptor_t* message_node; |
1159 | 0 | pbl_field_descriptor_t* field_node; |
1160 | 0 | pbl_enum_descriptor_t* enum_node; |
1161 | 0 | pbl_option_descriptor_t* option_node; |
1162 | 0 | pbl_node_t* node = (pbl_node_t*) anode; |
1163 | |
|
1164 | 0 | if (node == NULL) return; |
1165 | | |
1166 | 0 | switch (node->nodetype) { |
1167 | 0 | case PBL_METHOD: |
1168 | 0 | method_node = (pbl_method_descriptor_t*) node; |
1169 | 0 | g_free(method_node->in_msg_type); |
1170 | 0 | g_free(method_node->out_msg_type); |
1171 | 0 | break; |
1172 | 0 | case PBL_MESSAGE: |
1173 | 0 | message_node = (pbl_message_descriptor_t*) node; |
1174 | 0 | if (message_node->fields) { |
1175 | 0 | g_queue_free(message_node->fields); |
1176 | 0 | } |
1177 | 0 | if (message_node->fields_by_number) { |
1178 | 0 | g_hash_table_destroy(message_node->fields_by_number); |
1179 | 0 | } |
1180 | 0 | break; |
1181 | 0 | case PBL_FIELD: |
1182 | 0 | case PBL_MAP_FIELD: |
1183 | 0 | field_node = (pbl_field_descriptor_t*) node; |
1184 | 0 | g_free(field_node->type_name); |
1185 | 0 | if (field_node->orig_default_value) { |
1186 | 0 | g_free(field_node->orig_default_value); |
1187 | 0 | } |
1188 | 0 | if ((field_node->type == PROTOBUF_TYPE_STRING || field_node->type == PROTOBUF_TYPE_BYTES) |
1189 | 0 | && field_node->default_value.s) { |
1190 | 0 | g_free(field_node->default_value.s); |
1191 | 0 | } |
1192 | 0 | if (field_node->options_node) { |
1193 | | // We recurse here, but we're limited by depth checks at allocation time |
1194 | 0 | pbl_free_node(field_node->options_node); |
1195 | 0 | } |
1196 | 0 | break; |
1197 | 0 | case PBL_ENUM: |
1198 | 0 | enum_node = (pbl_enum_descriptor_t*) node; |
1199 | 0 | if (enum_node->values) { |
1200 | 0 | g_queue_free(enum_node->values); |
1201 | 0 | } |
1202 | 0 | if (enum_node->values_by_number) { |
1203 | 0 | g_hash_table_destroy(enum_node->values_by_number); |
1204 | 0 | } |
1205 | 0 | break; |
1206 | 0 | case PBL_OPTION: |
1207 | 0 | option_node = (pbl_option_descriptor_t*) node; |
1208 | 0 | g_free(option_node->value); |
1209 | 0 | break; |
1210 | 0 | default: |
1211 | | /* do nothing */ |
1212 | 0 | break; |
1213 | 0 | } |
1214 | | |
1215 | 0 | g_free(node->name); |
1216 | 0 | g_free(node->full_name); |
1217 | 0 | if (node->children) { |
1218 | 0 | g_queue_free_full(node->children, pbl_free_node); |
1219 | 0 | } |
1220 | 0 | if (node->children_by_name) { |
1221 | 0 | g_hash_table_destroy(node->children_by_name); |
1222 | 0 | } |
1223 | 0 | g_free(node); |
1224 | 0 | } |
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 | | */ |