GCC Code Coverage Report


Directory: src/gate/
File: src/gate/encode/yaml.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 0 898 0.0%
Functions: 0 28 0.0%
Branches: 0 456 0.0%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/encode/yaml.h"
30 #include "gate/results.h"
31 #include "gate/mathematics.h"
32 #include "gate/debugging.h"
33
34 static gate_string_t const indention_chars = GATE_STRING_INIT_STATIC(" \t");
35
36 #define LINE_TYPE_UNKNOWN 0
37 #define LINE_TYPE_EMPTY 1
38 #define LINE_TYPE_VALUE 2
39 #define LINE_TYPE_KEY 3 /* key-only entry, eg. "'key':" */
40 #define LINE_TYPE_MEMBER 4 /* key-value entry, eg. "'key': 'value'" */
41 #define LINE_TYPE_ARRAY_VALUE 5 /* array entry value, eg. "- 'value'" */
42 #define LINE_TYPE_ARRAY_KEY 6 /* array entry key-only, eg. "- 'key':" */
43 #define LINE_TYPE_ARRAY_MEMBER 7 /* array entry key-value, eg. "- 'key': 'value'" */
44
45
46 static char const* gate_yaml_bool_true_values[] =
47 {
48 "y", "Y", "yes", "Yes", "YES",
49 "true", "True", "TRUE",
50 "on", "On", "ON"
51 };
52
53 static char const* gate_yaml_bool_false_values[] =
54 {
55 "n", "N", "no", "No", "NO",
56 "false", "False", "FALSE",
57 "off", "Off", "OFF"
58 };
59
60 static gate_bool_t is_yaml_bool_true(gate_string_t const* str)
61 {
62 gate_size_t index;
63 gate_size_t len = sizeof(gate_yaml_bool_true_values) / sizeof(gate_yaml_bool_true_values[0]);
64 for (index = 0; index != len; ++index)
65 {
66 if (gate_string_equals_str(str, gate_yaml_bool_true_values[index]))
67 {
68 return true;
69 }
70 }
71 return false;
72 }
73 static gate_bool_t is_yaml_bool_false(gate_string_t const* str)
74 {
75 gate_size_t index;
76 gate_size_t len = sizeof(gate_yaml_bool_false_values) / sizeof(gate_yaml_bool_false_values[0]);
77 for (index = 0; index != len; ++index)
78 {
79 if (gate_string_equals_str(str, gate_yaml_bool_false_values[index]))
80 {
81 return true;
82 }
83 }
84 return false;
85 }
86 static gate_bool_t is_yaml_int(gate_string_t const* str, gate_int64_t* int_value)
87 {
88 gate_size_t len_parsed = gate_str_parse_int64(str->str, str->length, int_value);
89 return (len_parsed == str->length);
90 }
91 static gate_bool_t is_yaml_real(gate_string_t const* str, gate_real64_t* real_value)
92 {
93 gate_size_t len_parsed = gate_str_parse_real(str->str, str->length, real_value);
94 return (len_parsed == str->length);
95 }
96
97
98 static gate_size_t parse_quoted_string(gate_string_t const* source, gate_string_t* text)
99 {
100 gate_size_t parsed = 0;
101 char const* ptr = source->str;
102 gate_size_t len = source->length;
103 gate_strbuilder_t builder = GATE_INIT_EMPTY;
104 char quote_char;
105
106 do
107 {
108 quote_char = *ptr;
109 ++ptr;
110 --len;
111 ++parsed;
112
113 gate_strbuilder_create(&builder, len);
114
115 while (len != 0)
116 {
117 if (*ptr == '\\')
118 {
119 ++ptr;
120 --len;
121 ++parsed;
122 switch (*ptr)
123 {
124 case '\\':
125 gate_strbuilder_append_chars(&builder, 1, '\\');
126 break;
127 case '\"':
128 gate_strbuilder_append_chars(&builder, 1, '\"');
129 break;
130 case '\'':
131 gate_strbuilder_append_chars(&builder, 1, '\'');
132 break;
133 case 'r':
134 gate_strbuilder_append_chars(&builder, 1, '\r');
135 break;
136 case 'n':
137 gate_strbuilder_append_chars(&builder, 1, '\n');
138 break;
139 case 't':
140 gate_strbuilder_append_chars(&builder, 1, '\t');
141 break;
142 case 'v':
143 gate_strbuilder_append_chars(&builder, 1, '\v');
144 break;
145 case 'b':
146 gate_strbuilder_append_chars(&builder, 1, '\b');
147 break;
148 default:
149 gate_strbuilder_append_chars(&builder, 1, '\\');
150 gate_strbuilder_append_chars(&builder, 1, *ptr);
151 break;
152 }
153 }
154 else
155 {
156 if (*ptr == quote_char)
157 {
158 /* end of string reached */
159 ++parsed;
160 break;
161 }
162 else
163 {
164 gate_strbuilder_append_chars(&builder, 1, *ptr);
165 }
166 }
167
168 ++ptr;
169 --len;
170 ++parsed;
171 }
172 } while (0);
173
174 if (NULL == gate_strbuilder_to_string(&builder, text))
175 {
176 parsed = 0;
177 }
178 gate_strbuilder_release(&builder);
179
180 return parsed;
181 }
182
183 static gate_size_t skip_whitespaces(gate_string_t* text)
184 {
185 static gate_string_t white_spaces = GATE_STRING_INIT_STATIC(GATE_STR_WHITESPACES);
186 gate_size_t ret = gate_string_find_first_not_of(text, &white_spaces, 0);
187 if (ret == GATE_STR_NPOS)
188 {
189 ret = gate_string_length(text);
190 gate_string_release(text);
191 }
192 else
193 {
194 gate_string_substr(text, text, ret, GATE_STR_NPOS);
195 }
196 return ret;
197 }
198
199 static gate_size_t gate_yaml_parse_inline_object(gate_string_t const* token, gate_property_t* prop);
200 static gate_size_t gate_yaml_parse_inline_array(gate_string_t* token, gate_property_t* prop);
201
202
203 static gate_size_t gate_yaml_parse_inline_item(gate_string_t const* token, gate_property_t* prop, gate_string_t const* terminator_chars)
204 {
205 gate_size_t ret = 0;
206 gate_string_t text = GATE_STRING_INIT_EMPTY;
207 gate_string_t value = GATE_STRING_INIT_EMPTY;
208 gate_size_t pos = 0;
209 gate_int64_t i64 = 0;
210 gate_real64_t r64 = 0.0;
211 char chr;
212
213 do
214 {
215 gate_string_duplicate(&text, token);
216 ret += skip_whitespaces(&text);
217
218 if (gate_string_is_empty(&text))
219 {
220 ret = 0;
221 break;
222 }
223 chr = gate_string_char_at(&text, 0);
224 if ((chr == '\"') || (chr == '\''))
225 {
226 /* quoted string */
227 pos = parse_quoted_string(&text, &value);
228 if (pos == 0)
229 {
230 /* failed to parse quoted string */
231 GATE_DEBUG_TRACE("Failed to parse quoted YAML string");
232 ret = 0;
233 break;
234 }
235 if (NULL == gate_property_create_string(prop, &value))
236 {
237 GATE_DEBUG_TRACE("Failed to create string property object");
238 ret = 0;
239 break;
240 }
241 ret += pos;
242 }
243 else if (chr == '{')
244 {
245 /* inline object */
246 pos = gate_yaml_parse_inline_object(&text, prop);
247 if (pos == 0)
248 {
249 GATE_DEBUG_TRACE("Failed to parse inline YAML object");
250 ret = 0;
251 break;
252 }
253 ret += pos;
254 }
255 else if (chr == '[')
256 {
257 /* inline array */
258 pos = gate_yaml_parse_inline_array(&text, prop);
259 if (pos == 0)
260 {
261 GATE_DEBUG_TRACE("Failed to parse inline YAML array");
262 ret = 0;
263 break;
264 }
265 ret += pos;
266 }
267 else
268 {
269 /* primitive elment */
270 pos = gate_string_find_first_of(&text, terminator_chars, 0);
271 if (pos == GATE_STR_NPOS)
272 {
273 /* possibly the last element in document */
274 pos = gate_string_length(&text);
275 }
276 gate_string_substr(&value, &text, 0, pos);
277 ret += pos;
278 gate_string_trim(&value, &value);
279
280 if (is_yaml_bool_true(&value))
281 {
282 if (NULL == gate_property_create_bool(prop, true))
283 {
284 GATE_DEBUG_TRACE("Failed to create bool property ojbect");
285 ret = 0;
286 break;
287 }
288 }
289 else if (is_yaml_bool_false(&value))
290 {
291 if (NULL == gate_property_create_bool(prop, false))
292 {
293 GATE_DEBUG_TRACE("Failed to create bool property ojbect");
294 ret = 0;
295 break;
296 }
297 }
298 else if (is_yaml_int(&value, &i64))
299 {
300 if (NULL == gate_property_create_int(prop, i64))
301 {
302 GATE_DEBUG_TRACE("Failed to create integer property ojbect");
303 ret = 0;
304 break;
305 }
306 }
307 else if (is_yaml_real(&value, &r64))
308 {
309 if (NULL == gate_property_create_real(prop, r64))
310 {
311 GATE_DEBUG_TRACE("Failed to create real property ojbect");
312 ret = 0;
313 break;
314 }
315 }
316 else
317 {
318 /* OK, it is just a string */
319 if (NULL == gate_property_create_string(prop, &value))
320 {
321 GATE_DEBUG_TRACE("Failed to create string property ojbect");
322 ret = 0;
323 break;
324 }
325 }
326 }
327 } while (0);
328
329 gate_string_release(&text);
330 gate_string_release(&value);
331
332 return ret;
333 }
334
335 static gate_size_t gate_yaml_parse_inline_object(gate_string_t const* token, gate_property_t* prop)
336 {
337 static gate_string_t obj_item_terminator_chars = GATE_STRING_INIT_STATIC(",}");
338 gate_size_t ret = 0;
339 gate_string_t text = GATE_STRING_INIT_EMPTY;
340 gate_string_t member_name = GATE_STRING_INIT_EMPTY;
341 gate_string_t member_value = GATE_STRING_INIT_EMPTY;
342 gate_string_t tmp = GATE_STRING_INIT_EMPTY;
343 gate_bool_t succeeded;
344 gate_bool_t tmp_empty;
345 gate_property_t member_prop = GATE_INIT_EMPTY;
346 gate_size_t pos;
347 gate_string_duplicate(&text, token);
348
349 ret += skip_whitespaces(&text);
350
351 do
352 {
353 gate_mem_clear(prop, sizeof(gate_property_t));
354
355 if (!gate_string_starts_with_char(&text, '{'))
356 {
357 /* token does not start with object indicator */
358 GATE_DEBUG_TRACE("Missing YAML object indicator");
359 ret = 0;
360 break;
361 }
362 gate_string_substr(&text, &text, 1, GATE_STR_NPOS);
363 ++ret;
364
365 if (NULL == gate_property_create_object(prop))
366 {
367 /* failed to create property object */
368 GATE_DEBUG_TRACE("Failed to create object property object");
369 ret = 0;
370 break;
371 }
372
373 for (;;)
374 {
375 ret += skip_whitespaces(&text);
376 if (gate_string_is_empty(&text))
377 {
378 /* missing object content */
379 GATE_DEBUG_TRACE("Missing YAML object content");
380 ret = 0;
381 break;
382 }
383 if (gate_string_starts_with_char(&text, '}'))
384 {
385 /* end of object reached */
386 ++ret;
387 break;
388 }
389
390 /* parse key:*/
391 if (gate_string_starts_with_char(&text, '\"'))
392 {
393 pos = parse_quoted_string(&text, &member_name);
394 if (pos == 0)
395 {
396 GATE_DEBUG_TRACE("Failed to parse quoted YAML string");
397 ret = 0;
398 break;
399 }
400 ret += pos;
401 gate_string_substr(&text, &text, pos, GATE_STR_NPOS);
402 pos = gate_string_char_pos(&text, ':', 0);
403 if (pos == GATE_STR_NPOS)
404 {
405 GATE_DEBUG_TRACE("Missing YAML object key/value separator");
406 ret = 0;
407 break;
408 }
409 gate_string_substr(&text, &text, pos + 1, GATE_STR_NPOS);
410 ret += (pos + 1);
411 }
412 else
413 {
414 pos = gate_string_char_pos(&text, ':', 0);
415 if (pos == GATE_STR_NPOS)
416 {
417 GATE_DEBUG_TRACE("Missing YAML object key/value separator");
418 ret = 0;
419 break;
420 }
421 gate_string_substr(&member_name, &text, 0, pos);
422 gate_string_substr(&text, &text, pos + 1, GATE_STR_NPOS);
423 ret += (pos + 1);
424 }
425
426 /* parse value: */
427 ret += skip_whitespaces(&text);
428 pos = gate_yaml_parse_inline_item(&text, &member_prop, &obj_item_terminator_chars);
429 if (pos == 0)
430 {
431 /* failed to parse member property */
432 GATE_DEBUG_TRACE("Failed to parse YAML object member value");
433 ret = 0;
434 break;
435 }
436 ret += pos;
437 gate_string_substr(&text, &text, pos, GATE_STR_NPOS);
438
439 succeeded = (NULL != gate_property_member_add(prop, &member_name, &member_prop));
440 gate_string_release(&member_name);
441 gate_property_destroy(&member_prop);
442 if (!succeeded)
443 {
444 /* failed to add new property to object */
445 GATE_DEBUG_TRACE("Failed to add key/value member to property object");
446 ret = 0;
447 break;
448 }
449
450 pos = gate_string_find_first_of(&text, &obj_item_terminator_chars, 0);
451 if (pos == GATE_STR_NPOS)
452 {
453 /* broken object -> corruption */
454 GATE_DEBUG_TRACE("Missing YAML object member seperator or terminator");
455 ret = 0;
456 break;
457 }
458 gate_string_substr(&tmp, &text, 0, pos);
459 ret += pos;
460 gate_string_substr(&text, &text, pos, GATE_STR_NPOS);
461
462 skip_whitespaces(&tmp);
463 tmp_empty = gate_string_is_empty(&tmp);
464 gate_string_release(&tmp);
465 if (!tmp_empty)
466 {
467 /* no non-whitespace-chars allowed until terminator chars show up */
468 GATE_DEBUG_TRACE("Unexpected characters after YAML member value detected");
469 ret = 0;
470 break;
471 }
472 if (gate_string_starts_with_char(&text, '}'))
473 {
474 /* end of object reached */
475 ++ret;
476 break;
477 }
478 if (!gate_string_starts_with_char(&text, ','))
479 {
480 /* object corrupted */
481 GATE_DEBUG_TRACE("Missing YAML object member separator");
482 ret = 0;
483 break;
484 }
485 else
486 {
487 /* another key/value pair is showing up */
488 ++ret;
489 gate_string_substr(&text, &text, 1, GATE_STR_NPOS);
490 }
491 }
492 } while (0);
493
494 if (ret == 0)
495 {
496 gate_property_destroy(prop);
497 }
498
499 gate_string_release(&tmp);
500 gate_string_release(&member_name);
501 gate_string_release(&member_value);
502 gate_property_destroy(&member_prop);
503 gate_string_release(&text);
504 return ret;
505 }
506 static gate_size_t gate_yaml_parse_inline_array(gate_string_t* token, gate_property_t* prop)
507 {
508 static gate_string_t arr_item_terminator_chars = GATE_STRING_INIT_STATIC(",]");
509 gate_size_t ret = 0;
510 gate_string_t text = GATE_STRING_INIT_EMPTY;
511 gate_string_t tmp = GATE_STRING_INIT_EMPTY;
512 gate_bool_t succeeded;
513 gate_bool_t tmp_empty;
514 gate_property_t item_prop = GATE_INIT_EMPTY;
515 gate_size_t pos;
516 gate_string_duplicate(&text, token);
517
518 ret += skip_whitespaces(&text);
519
520 do
521 {
522 gate_mem_clear(prop, sizeof(gate_property_t));
523
524 if (!gate_string_starts_with_char(&text, '['))
525 {
526 /* token does not start with an array indicator */
527 GATE_DEBUG_TRACE("Missing YAML array indicator");
528 ret = 0;
529 break;
530 }
531 gate_string_substr(&text, &text, 1, GATE_STR_NPOS);
532 ++ret;
533
534 if (NULL == gate_property_create_array(prop, NULL, 0))
535 {
536 GATE_DEBUG_TRACE("Failed to create array property object");
537 ret = 0;
538 break;
539 }
540
541 for (;;)
542 {
543 ret += skip_whitespaces(&text);
544 if (gate_string_is_empty(&text))
545 {
546 /* missing array content */
547 GATE_DEBUG_TRACE("Missing YAML array content");
548 ret = 0;
549 break;
550 }
551 if (gate_string_starts_with_char(&text, ']'))
552 {
553 /* end of array reached */
554 ++ret;
555 break;
556 }
557
558 /* parse value: */
559 ret += skip_whitespaces(&text);
560 pos = gate_yaml_parse_inline_item(&text, &item_prop, &arr_item_terminator_chars);
561 if (pos == 0)
562 {
563 GATE_DEBUG_TRACE("Failed to parse YAML array item");
564 /* failed to parse member property */
565 ret = 0;
566 break;
567 }
568 ret += pos;
569 gate_string_substr(&text, &text, pos, GATE_STR_NPOS);
570
571 succeeded = (NULL != gate_property_array_add(prop, &item_prop));
572 gate_property_destroy(&item_prop);
573 if (!succeeded)
574 {
575 GATE_DEBUG_TRACE("Failed to add item to array property object");
576 /* failed to add new item to array */
577 ret = 0;
578 break;
579 }
580
581 pos = gate_string_find_first_of(&text, &arr_item_terminator_chars, 0);
582 if (pos == GATE_STR_NPOS)
583 {
584 GATE_DEBUG_TRACE("Missing YAML array item separator or terminator");
585 /* broken array -> corruption */
586 ret = 0;
587 break;
588 }
589 gate_string_substr(&tmp, &text, 0, pos);
590 ret += pos;
591 gate_string_substr(&text, &text, pos, GATE_STR_NPOS);
592
593 skip_whitespaces(&tmp);
594 tmp_empty = gate_string_is_empty(&tmp);
595 gate_string_release(&tmp);
596 if (!tmp_empty)
597 {
598 GATE_DEBUG_TRACE("Unexpected characters after YAML array item detected");
599 /* no non-whitespace-chars allowed until terminator chars show up */
600 ret = 0;
601 break;
602 }
603 if (gate_string_starts_with_char(&text, ']'))
604 {
605 /* end of object reached */
606 ++ret;
607 break;
608 }
609 if (!gate_string_starts_with_char(&text, ','))
610 {
611 GATE_DEBUG_TRACE("Missing YAML array separator");
612 /* object corrupted */
613 ret = 0;
614 break;
615 }
616 else
617 {
618 /* another key/value pair is showing up */
619 ++ret;
620 gate_string_substr(&text, &text, 1, GATE_STR_NPOS);
621 }
622 }
623 } while (0);
624
625 if (ret == 0)
626 {
627 gate_property_destroy(prop);
628 }
629
630 gate_string_release(&tmp);
631 gate_property_destroy(&item_prop);
632 gate_string_release(&text);
633 return ret;
634 }
635
636 /*
637 static gate_property_t* gate_yaml_parse_inline(gate_string_t* line, gate_property_t* prop)
638 {
639 gate_property_t* ret = NULL;
640 gate_size_t parsed = 0;
641
642 if(gate_string_starts_with_char(line, '{'))
643 {
644 parsed = gate_yaml_parse_inline_object(line, prop);
645 if(parsed != 0)
646 {
647 ret = prop;
648 }
649 }
650 else if(gate_string_starts_with_char(line, '['))
651 {
652 parsed = gate_yaml_parse_inline_array(line, prop);
653 if(parsed != 0)
654 {
655 ret = prop;
656 }
657 }
658
659 return ret;
660 }
661 */
662
663 static gate_property_t* gate_yaml_parse_entry(gate_string_t* line, gate_property_t* prop, gate_bool_t* incomplete)
664 {
665 static gate_string_t end_of_line = GATE_STRING_INIT_STATIC("\n");
666 gate_property_t* ret = NULL;
667 gate_string_t text = GATE_STRING_INIT_EMPTY;
668 gate_property_t subprop = GATE_INIT_EMPTY;
669 gate_size_t parsed;
670 gate_size_t pos;
671 gate_bool_t valueonly = false;
672 gate_bool_t keyonly = false;
673 gate_bool_t keyvalue = false;
674 gate_bool_t subprop_incomplete = false;
675 gate_bool_t quoted_value = false;
676 gate_int64_t i64 = 0;
677 gate_real64_t r64 = 0.0;
678 do
679 {
680 gate_string_ltrim(line, line);
681 if (gate_string_is_empty(line))
682 {
683 GATE_DEBUG_TRACE("Missing YAML line content");
684 /* error, nothing to parse */
685 break;
686 }
687
688 if (gate_string_starts_with_char(line, '{') || gate_string_starts_with_char(line, '['))
689 {
690 /* inline object or inline array */
691 *incomplete = false;
692 pos = gate_yaml_parse_inline_item(line, prop, &end_of_line);
693 if (pos == 0)
694 {
695 GATE_DEBUG_TRACE("Failed to parse inline YAML entry");
696 ret = NULL;
697 }
698 else
699 {
700 ret = prop;
701 }
702 break;
703 }
704
705 if (gate_string_starts_with_char(line, '\"') || gate_string_starts_with_char(line, '\''))
706 {
707 /* quoted string */
708 parsed = parse_quoted_string(line, &text);
709 if (parsed == 0)
710 {
711 GATE_DEBUG_TRACE("Failed to parse quoted YAML string");
712 break;
713 }
714 gate_string_substr(line, line, parsed, GATE_STR_NPOS);
715 gate_string_ltrim(line, line);
716 if (gate_string_starts_with_char(line, ':'))
717 {
718 /* key - value pair: */
719 gate_string_substr(line, line, 1, GATE_STR_NPOS);
720 gate_string_ltrim(line, line);
721 if (gate_string_is_empty(line))
722 {
723 keyonly = true;
724 }
725 else
726 {
727 if (NULL == gate_yaml_parse_entry(line, &subprop, &subprop_incomplete))
728 {
729 GATE_DEBUG_TRACE("Failed to parse YAML entry member value");
730 /* error case: failed to parse sub-property */
731 break;
732 }
733 if (subprop_incomplete)
734 {
735 GATE_DEBUG_TRACE("Incomplete sub property dtected");
736 /* error case: sub-property is incomplete*/
737 break;
738 }
739 keyvalue = true;
740 }
741 }
742 else
743 {
744 /* value-only */
745 valueonly = true;
746 quoted_value = true;
747 }
748 }
749 else
750 {
751 pos = gate_string_char_pos(line, ':', 0);
752 if (pos == GATE_STR_NPOS)
753 {
754 gate_string_duplicate(&text, line);
755 valueonly = true;
756 /* value only */
757 }
758 else
759 {
760 gate_string_substr(&text, line, 0, pos);
761 gate_string_ltrim(&text, &text);
762 gate_string_substr(line, line, pos + 1, GATE_STR_NPOS);
763 gate_string_ltrim(line, line);
764
765 if (gate_string_is_empty(line))
766 {
767 keyonly = true;
768 }
769 else
770 {
771 if (NULL == gate_yaml_parse_entry(line, &subprop, &subprop_incomplete))
772 {
773 GATE_DEBUG_TRACE("Failed to parse YAML entry member value");
774 /* error case: failed to parse sub-property */
775 break;
776 }
777 if (subprop_incomplete)
778 {
779 GATE_DEBUG_TRACE("Incomplete sub property dtected");
780 /* error case: sub-property is incomplete*/
781 break;
782 }
783 keyvalue = true;
784 }
785 }
786 }
787
788 *incomplete = keyonly;
789
790 if (valueonly)
791 {
792 switch (gate_property_get_type(prop))
793 {
794 case GATE_PROPERTY_TYPE_ARRAY:
795 {
796 GATE_DEBUG_TRACE("Internal error, property of type array detected");
797 /* error case: arrays are handled outside */
798 break;
799 }
800 case GATE_PROPERTY_TYPE_OBJECT:
801 {
802 GATE_DEBUG_TRACE("Internal error, property of type object detected");
803 /* error case: cannot attach value-only to object*/
804 break;
805 }
806 default:
807 {
808 gate_property_destroy(prop);
809 if (quoted_value)
810 {
811 ret = gate_property_create_string(prop, &text);
812 }
813 else
814 {
815 if (is_yaml_bool_true(&text))
816 {
817 ret = gate_property_create_bool(prop, true);
818 }
819 else if (is_yaml_bool_false(&text))
820 {
821 ret = gate_property_create_bool(prop, false);
822 }
823 else if (is_yaml_int(&text, &i64))
824 {
825 ret = gate_property_create_int(prop, i64);
826 }
827 else if (is_yaml_real(&text, &r64))
828 {
829 ret = gate_property_create_real(prop, r64);
830 }
831 else
832 {
833 ret = gate_property_create_string(prop, &text);
834 }
835 }
836
837 break;
838 }
839 }
840 }
841 else if (keyonly || keyvalue)
842 {
843 switch (gate_property_get_type(prop))
844 {
845 case GATE_PROPERTY_TYPE_ARRAY:
846 {
847 GATE_DEBUG_TRACE("Internal error, property of type array detected");
848 /* error case: arrays are handled outside */
849 break;
850 }
851 case GATE_PROPERTY_TYPE_OBJECT:
852 {
853 ret = gate_property_member_add(prop, &text, &subprop);
854 break;
855 }
856 default:
857 {
858 gate_property_destroy(prop);
859 ret = gate_property_create_object(prop);
860 if (ret != NULL)
861 {
862 if (NULL == gate_property_member_add(prop, &text, &subprop))
863 {
864 gate_property_destroy(prop);
865 ret = NULL;
866 }
867 }
868 break;
869 }
870 }
871 }
872 else
873 {
874 /* error case: unsupported behavior */
875 break;
876 }
877
878 } while (0);
879
880 gate_string_release(&text);
881 gate_property_destroy(&subprop);
882
883 return ret;
884 }
885
886 static gate_result_t gate_yaml_parse_indent(gate_string_t* line, gate_uint16_t* indent)
887 {
888 gate_result_t ret = GATE_RESULT_FAILED;
889 gate_size_t pos;
890 pos = gate_string_find_first_not_of(line, &indention_chars, 0);
891 if (pos == GATE_STR_NPOS)
892 {
893 /* impossible, should be detected by is_empty() */
894 GATE_DEBUG_TRACE("Internal error, missing indention");
895 ret = GATE_RESULT_CRITICALERROR;
896 }
897 else
898 {
899 *indent = (gate_uint16_t)pos;
900 gate_string_substr(line, line, pos, GATE_STR_NPOS);
901 ret = GATE_RESULT_OK;
902 }
903 return ret;
904 }
905
906
907 #define GATE_YAML_MAX_LEVELS 64
908 #define GATE_YAML_UNKNOWN_INDENTION ((gate_uint16_t)(-1))
909
910 static gate_result_t gate_yaml_merge_properties(gate_property_t* target, gate_property_t const* source, gate_bool_t source_is_array_entry)
911 {
912 gate_result_t ret = GATE_RESULT_FAILED;
913 gate_uint32_t target_type = gate_property_get_type(target);
914 gate_uint32_t source_type = gate_property_get_type(source);
915 gate_size_t index;
916 gate_size_t len;
917 gate_array_t name_array = GATE_INIT_EMPTY;
918 gate_string_t const* ptr_name;
919 gate_property_t const* ptr_prop;
920
921 if (source_type == GATE_PROPERTY_TYPE_EMPTY)
922 {
923 /* nothing to do */
924 ret = GATE_RESULT_OK;
925 }
926 switch (target_type)
927 {
928 case GATE_PROPERTY_TYPE_EMPTY:
929 {
930 if (source_is_array_entry)
931 {
932 if (NULL == gate_property_create_array(target, source, 1))
933 {
934 ret = GATE_RESULT_OUTOFMEMORY;
935 }
936 else
937 {
938 ret = GATE_RESULT_OK;
939 }
940 }
941 else
942 {
943 if (NULL == gate_property_copy(target, source))
944 {
945 ret = GATE_RESULT_OUTOFMEMORY;
946 }
947 else
948 {
949 ret = GATE_RESULT_OK;
950 }
951 }
952 break;
953 }
954 case GATE_PROPERTY_TYPE_BOOL:
955 case GATE_PROPERTY_TYPE_INT:
956 case GATE_PROPERTY_TYPE_REAL:
957 {
958 if (source_type == target_type)
959 {
960 if (NULL == gate_property_copy(target, source))
961 {
962 ret = GATE_RESULT_OUTOFMEMORY;
963 }
964 else
965 {
966 ret = GATE_RESULT_OK;
967 }
968 }
969 else
970 {
971 ret = GATE_RESULT_INCORRECTTYPE;
972 }
973 break;
974 }
975 case GATE_PROPERTY_TYPE_STRING:
976 {
977 if (source_type == target_type)
978 {
979 ret = gate_property_set_string(target, &source->data.string_value);
980 }
981 else
982 {
983 ret = GATE_RESULT_INCORRECTTYPE;
984 }
985 break;
986 }
987 case GATE_PROPERTY_TYPE_ARRAY:
988 {
989 if (!source_is_array_entry && (source_type == GATE_PROPERTY_TYPE_OBJECT))
990 {
991 /* this might be an additional object member entry for the previously added object */
992 len = gate_property_array_length(target);
993 if (len > 0)
994 {
995 ptr_prop = gate_property_array_get(target, len - 1);
996 if (ptr_prop)
997 {
998 if (gate_property_get_type(ptr_prop) == GATE_PROPERTY_TYPE_OBJECT)
999 {
1000 /* add new member to last object-entry in array */
1001 ret = gate_yaml_merge_properties((gate_property_t*)ptr_prop, source, false);
1002 break;
1003 }
1004 }
1005 }
1006
1007 }
1008 /* otherwise just add array item */
1009 if (NULL == gate_property_array_add(target, source))
1010 {
1011 ret = GATE_RESULT_OUTOFMEMORY;
1012 }
1013 else
1014 {
1015 ret = GATE_RESULT_OK;
1016 }
1017 break;
1018 }
1019 case GATE_PROPERTY_TYPE_OBJECT:
1020 {
1021 if (source_is_array_entry)
1022 {
1023 ret = GATE_RESULT_INCORRECTTYPE;
1024 break;
1025 }
1026 if (NULL == gate_property_member_names(source, &name_array))
1027 {
1028 ret = GATE_RESULT_OUTOFMEMORY;
1029 }
1030 else
1031 {
1032 ret = GATE_RESULT_OK;
1033 len = gate_array_length(&name_array);
1034 for (index = 0; index != len; ++index)
1035 {
1036 ptr_name = gate_array_get(&name_array, index);
1037 if (ptr_name)
1038 {
1039 ptr_prop = gate_property_member_get(source, ptr_name);
1040 if (ptr_prop)
1041 {
1042 if (NULL == gate_property_member_add(target, ptr_name, ptr_prop))
1043 {
1044 ret = GATE_RESULT_FAILED;
1045 break;
1046 }
1047 }
1048 }
1049 }
1050 gate_array_release(&name_array);
1051 }
1052 break;
1053 }
1054 default:
1055 {
1056 ret = GATE_RESULT_NOTSUPPORTED;
1057 break;
1058 }
1059 }
1060 return ret;
1061 }
1062
1063 static gate_result_t gate_yaml_integrate(gate_property_t* target, gate_string_t const* key, gate_property_t const* source)
1064 {
1065 gate_uint32_t target_type = gate_property_get_type(target);
1066 gate_size_t len;
1067 gate_property_t* ptr_prop;
1068
1069 if (target_type == GATE_PROPERTY_TYPE_EMPTY)
1070 {
1071 if (NULL == gate_property_create_object(target))
1072 {
1073 return GATE_RESULT_OUTOFMEMORY;
1074 }
1075 target_type = GATE_PROPERTY_TYPE_OBJECT;
1076 }
1077
1078 switch (target_type)
1079 {
1080 case GATE_PROPERTY_TYPE_ARRAY:
1081 {
1082 /* check if target is an array of objects -> add member to last object */
1083 len = gate_property_array_length(target);
1084 if (len > 0)
1085 {
1086 ptr_prop = (gate_property_t*)gate_property_array_get(target, len - 1);
1087 if (ptr_prop)
1088 {
1089 if (gate_property_get_type(ptr_prop) == GATE_PROPERTY_TYPE_OBJECT)
1090 {
1091 if (NULL != gate_property_member_add(ptr_prop, key, source))
1092 {
1093 return GATE_RESULT_OK;
1094 }
1095 else
1096 {
1097 return GATE_RESULT_OUTOFMEMORY;
1098 }
1099 }
1100 }
1101 }
1102 return GATE_RESULT_INVALIDSTATE;
1103 }
1104 case GATE_PROPERTY_TYPE_OBJECT:
1105 {
1106 if (NULL != gate_property_member_add(target, key, source))
1107 {
1108 return GATE_RESULT_OK;
1109 }
1110 else
1111 {
1112 return GATE_RESULT_OUTOFMEMORY;
1113 }
1114 }
1115 default:
1116 {
1117 return GATE_RESULT_INCORRECTTYPE;
1118 }
1119 }
1120 }
1121
1122 static gate_result_t gate_yaml_extract_name_of_incomplete_object(gate_property_t const* prop, gate_string_t* name)
1123 {
1124 gate_result_t ret = GATE_RESULT_FAILED;
1125 gate_uint32_t prop_type;
1126 gate_array_t prop_names;
1127 gate_string_t const* ptr_name;
1128 do
1129 {
1130 prop_type = gate_property_get_type(prop);
1131 switch (prop_type)
1132 {
1133 case GATE_PROPERTY_TYPE_OBJECT:
1134 {
1135 if (NULL != gate_property_member_names(prop, &prop_names))
1136 {
1137 ptr_name = gate_array_get(&prop_names, 0);
1138 if (ptr_name)
1139 {
1140 if (NULL != gate_string_duplicate(name, ptr_name))
1141 {
1142 ret = GATE_RESULT_OK;
1143 }
1144 }
1145 gate_array_release(&prop_names);
1146 }
1147 break;
1148 }
1149 case GATE_PROPERTY_TYPE_STRING:
1150 {
1151 ret = gate_property_get_string(prop, name);
1152 break;
1153 }
1154 default:
1155 {
1156 ret = GATE_RESULT_INCORRECTTYPE;
1157 break;
1158 }
1159 }
1160 } while (0);
1161
1162 return ret;
1163 }
1164
1165 #define GATE_YAML_UNKNOWN_NEXT_INDENT 65535
1166
1167
1168 gate_result_t gate_yaml_parse_string(gate_string_t const* source, gate_property_t* target_prop, gate_yaml_result_t* result)
1169 {
1170 gate_property_t levels[GATE_YAML_MAX_LEVELS];
1171 gate_uint16_t indents[GATE_YAML_MAX_LEVELS] = GATE_INIT_EMPTY;
1172 gate_string_t keys[GATE_YAML_MAX_LEVELS] = GATE_INIT_EMPTY;
1173 gate_size_t const max_levels = GATE_YAML_MAX_LEVELS;
1174 gate_size_t current_level = 0;
1175 gate_size_t index;
1176 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1177 gate_string_t line = GATE_STRING_INIT_EMPTY;
1178 gate_string_t yaml = GATE_STRING_INIT_EMPTY;
1179 gate_uint16_t current_indent;
1180 gate_property_t entry_prop = GATE_INIT_EMPTY;
1181 gate_bool_t entry_incomplete = false;
1182 gate_bool_t entry_is_array = false;
1183 gate_uint16_t array_indent = 0;
1184 gate_size_t pos;
1185 gate_size_t lines_parsed = 0;
1186
1187 do
1188 {
1189 gate_mem_clear(levels, sizeof(levels));
1190 gate_mem_clear(result, sizeof(gate_yaml_result_t));
1191
1192 if (NULL == gate_string_duplicate(&yaml, source))
1193 {
1194 ret = GATE_RESULT_OUTOFMEMORY;
1195 break;
1196 }
1197
1198 indents[0] = GATE_YAML_UNKNOWN_NEXT_INDENT;
1199 current_level = 0;
1200
1201 while (gate_string_read_line(&line, &yaml, &yaml))
1202 {
1203 ++lines_parsed;
1204 gate_string_rtrim(&line, &line);
1205 if (gate_string_is_empty(&line))
1206 {
1207 /* skip empty lines */
1208 continue;
1209 }
1210
1211 ret = gate_yaml_parse_indent(&line, &current_indent);
1212 GATE_BREAK_IF_FAILED(ret);
1213
1214 entry_is_array = false;
1215 if (gate_string_starts_with_char(&line, '-'))
1216 {
1217 /* array entry: */
1218 pos = gate_string_find_first_not_of(&line, &indention_chars, 1);
1219 if (pos == GATE_STR_NPOS)
1220 {
1221 /* error case: empty array entry */
1222 ret = GATE_RESULT_INVALIDDATA;
1223 break;
1224 }
1225 entry_is_array = true;
1226 array_indent = current_indent;
1227 current_indent += (gate_uint16_t)pos;
1228 gate_string_substr(&line, &line, pos, GATE_STR_NPOS);
1229 }
1230
1231 if (NULL == gate_yaml_parse_entry(&line, &entry_prop, &entry_incomplete))
1232 {
1233 ret = GATE_RESULT_FAILED;
1234 break;
1235 }
1236
1237 if (indents[current_level] == GATE_YAML_UNKNOWN_NEXT_INDENT)
1238 {
1239 if ((current_level > 0) && (current_indent < indents[current_level - 1]))
1240 {
1241 /* new advanced indention must be greater than parent element */
1242 ret = GATE_RESULT_INVALIDHEADER;
1243 break;
1244 }
1245 indents[current_level] = current_indent;
1246 }
1247
1248
1249 while (current_indent < indents[current_level])
1250 {
1251 if (current_level == 0)
1252 {
1253 ret = GATE_RESULT_CRITICALERROR;
1254 break;
1255 }
1256
1257 ret = gate_yaml_integrate(&levels[current_level - 1], &keys[current_level], &levels[current_level]);
1258 GATE_BREAK_IF_FAILED(ret);
1259 gate_property_destroy(&levels[current_level]);
1260 gate_string_release(&keys[current_level]);
1261 indents[current_level] = GATE_YAML_UNKNOWN_NEXT_INDENT;
1262 --current_level;
1263 }
1264 GATE_BREAK_IF_FAILED(ret);
1265
1266 if (current_indent > indents[current_level])
1267 {
1268 /* unexpected indention */
1269 ret = GATE_RESULT_INVALIDHEADER;
1270 break;
1271 }
1272
1273 if (entry_incomplete)
1274 {
1275 ++current_level;
1276 gate_property_create_empty(&levels[current_level]);
1277 indents[current_level] = GATE_YAML_UNKNOWN_NEXT_INDENT;
1278 ret = gate_yaml_extract_name_of_incomplete_object(&entry_prop, &keys[current_level]);
1279 }
1280 else
1281 {
1282 ret = gate_yaml_merge_properties(&levels[current_level], &entry_prop, entry_is_array);
1283 }
1284 GATE_BREAK_IF_FAILED(ret);
1285
1286 gate_property_destroy(&entry_prop);
1287 gate_string_release(&line);
1288 }
1289
1290 } while (0);
1291
1292 if (GATE_SUCCEEDED(ret))
1293 {
1294 while (current_level > 0)
1295 {
1296 ret = gate_yaml_integrate(&levels[current_level - 1], &keys[current_level], &levels[current_level]);
1297 GATE_BREAK_IF_FAILED(ret);
1298 gate_property_destroy(&levels[current_level]);
1299 gate_string_release(&keys[current_level]);
1300 indents[current_level] = GATE_YAML_UNKNOWN_NEXT_INDENT;
1301 --current_level;
1302 }
1303 if (GATE_SUCCEEDED(ret))
1304 {
1305 gate_mem_copy(target_prop, &levels[0], sizeof(gate_property_t));
1306 gate_mem_clear(&levels[0], sizeof(gate_property_t));
1307 }
1308 }
1309
1310 if (result != NULL)
1311 {
1312 gate_mem_clear(result, sizeof(gate_yaml_result_t));
1313 if (GATE_SUCCEEDED(ret))
1314 {
1315 result->succeeded = true;
1316 }
1317 result->lines_processed = lines_parsed;
1318 result->chars_processed = 0;
1319 }
1320
1321 gate_property_destroy(&entry_prop);
1322 gate_string_release(&line);
1323 gate_string_release(&yaml);
1324
1325 for (index = 0; index != max_levels; ++index)
1326 {
1327 gate_property_destroy(&levels[index]);
1328 gate_string_release(&keys[index]);
1329 }
1330 return ret;
1331 }
1332
1333 gate_result_t gate_yaml_parse(gate_stream_t* input_source, gate_property_t* target_prop, gate_yaml_result_t* result)
1334 {
1335 gate_result_t ret = GATE_RESULT_FAILED;
1336 gate_stringstream_t* str_stream = NULL;
1337 gate_string_t str = GATE_STRING_INIT_EMPTY;
1338 do
1339 {
1340 str_stream = gate_stringstream_create(256);
1341 if (str_stream == NULL)
1342 {
1343 ret = GATE_RESULT_OUTOFMEMORY;
1344 break;
1345 }
1346 ret = gate_stream_transfer(input_source, (gate_stream_t*)str_stream);
1347 GATE_BREAK_IF_FAILED(ret);
1348
1349 ret = gate_stringstream_to_string(str_stream, &str);
1350 GATE_BREAK_IF_FAILED(ret);
1351
1352 ret = gate_yaml_parse_string(&str, target_prop, result);
1353 } while (0);
1354
1355 gate_string_release(&str);
1356
1357 if (str_stream != NULL)
1358 {
1359 gate_object_release(str_stream);
1360 }
1361 return ret;
1362 }
1363
1364
1365
1366 static gate_result_t gate_yaml_print_bool(gate_stream_t* stream, gate_bool_t value)
1367 {
1368 return gate_stream_print_cstr(stream, value ? "true" : "false");
1369 }
1370
1371 static gate_result_t gate_yaml_print_int(gate_stream_t* stream, gate_int64_t value)
1372 {
1373 return gate_stream_print_int64(stream, value);
1374 }
1375
1376 static gate_result_t gate_yaml_print_real(gate_stream_t* stream, gate_real64_t value)
1377 {
1378 return gate_stream_print_real(stream, value, 0, gate_math_decimal_length(value), 0);
1379 }
1380
1381 static gate_result_t gate_yaml_print_string(gate_stream_t* stream, gate_string_t const* value)
1382 {
1383 gate_result_t ret = GATE_RESULT_FAILED;
1384 char const* ptr;
1385 gate_size_t len;
1386 gate_size_t written;
1387 char chr;
1388
1389 if (gate_string_count_chars(value, ' ')
1390 + gate_string_count_chars(value, ':')
1391 + gate_string_count_chars(value, '\"')
1392 + gate_string_count_chars(value, '\'')
1393 + gate_string_count_chars(value, '\n')
1394 )
1395 {
1396 do
1397 {
1398 ret = gate_stream_write(stream, "\"", 1, &written);
1399 GATE_BREAK_IF_FAILED(ret);
1400
1401 if (value)
1402 {
1403 ptr = value->str;
1404 len = value->length;
1405 while (len-- != 0)
1406 {
1407 chr = *ptr;
1408 switch (chr)
1409 {
1410 case '\"':
1411 case '\'':
1412 ret = gate_stream_write_block(stream, "\\", 1, &written);
1413 break;
1414 case '\r':
1415 ret = gate_stream_write_block(stream, "\\", 1, &written);
1416 chr = 'r';
1417 break;
1418 case '\n':
1419 ret = gate_stream_write_block(stream, "\\", 1, &written);
1420 chr = 'n';
1421 break;
1422 default:
1423 break;
1424 }
1425 ret = gate_stream_write(stream, &chr, 1, &written);
1426 ++ptr;
1427 }
1428 }
1429
1430 ret = gate_stream_write(stream, "\"", 1, &written);
1431 GATE_BREAK_IF_FAILED(ret);
1432 } while (0);
1433 return ret;
1434 }
1435 else
1436 {
1437 return gate_stream_print_string(stream, value);
1438 }
1439 }
1440
1441 static gate_result_t gate_yaml_print_inline(gate_property_t const* prop, gate_stream_t* stream);
1442
1443 static gate_result_t gate_yaml_print_array(gate_stream_t* stream, gate_arraylist_t value)
1444 {
1445 gate_result_t ret;
1446 gate_size_t count = gate_arraylist_length(value);
1447 gate_size_t index;
1448 gate_property_t const* prop;
1449 do
1450 {
1451 ret = gate_stream_print_cstr(stream, "[ ");
1452 GATE_BREAK_IF_FAILED(ret);
1453
1454 for (index = 0; index != count; ++index)
1455 {
1456 if (index != 0)
1457 {
1458 ret = gate_stream_print_cstr(stream, ", ");
1459 GATE_BREAK_IF_FAILED(ret);
1460 }
1461
1462 prop = (gate_property_t const*)gate_arraylist_get(value, index);
1463 if (prop != NULL)
1464 {
1465 ret = gate_yaml_print_inline(prop, stream);
1466 GATE_BREAK_IF_FAILED(ret);
1467 }
1468 }
1469 GATE_BREAK_IF_FAILED(ret);
1470
1471 ret = gate_stream_print_cstr(stream, " ]");
1472 GATE_BREAK_IF_FAILED(ret);
1473
1474 } while (0);
1475 return ret;
1476 }
1477
1478 static gate_result_t gate_yaml_print_object(gate_stream_t* stream, gate_property_t const* value)
1479 {
1480 gate_result_t ret;
1481 gate_size_t count;
1482 gate_size_t index;
1483 gate_array_t names = GATE_INIT_EMPTY;
1484 gate_property_t const* prop;
1485 gate_string_t const* ptr_name;
1486
1487 do
1488 {
1489 if (!gate_property_member_names(value, &names))
1490 {
1491 ret = GATE_RESULT_INVALIDCONTENT;
1492 break;
1493 }
1494
1495 count = gate_array_length(&names);
1496
1497 ret = gate_stream_print_cstr(stream, "{ ");
1498 GATE_BREAK_IF_FAILED(ret);
1499
1500 for (index = 0; index != count; ++index)
1501 {
1502 ptr_name = (gate_string_t const*)gate_array_get(&names, index);
1503 if (ptr_name)
1504 {
1505 prop = gate_property_member_get(value, ptr_name);
1506 if (prop)
1507 {
1508 if (index != 0)
1509 {
1510 ret = gate_stream_print_cstr(stream, ", ");
1511 GATE_BREAK_IF_FAILED(ret);
1512 }
1513 ret = gate_yaml_print_string(stream, ptr_name);
1514 GATE_BREAK_IF_FAILED(ret);
1515 ret = gate_stream_print_cstr(stream, ": ");
1516 GATE_BREAK_IF_FAILED(ret);
1517 ret = gate_yaml_print_inline(prop, stream);
1518 GATE_BREAK_IF_FAILED(ret);
1519 }
1520 }
1521 }
1522 GATE_BREAK_IF_FAILED(ret);
1523
1524 ret = gate_stream_print_cstr(stream, " }");
1525 GATE_BREAK_IF_FAILED(ret);
1526
1527 } while (0);
1528
1529 gate_array_release(&names);
1530 return ret;
1531 }
1532
1533 /*
1534 static gate_result_t gate_yaml_indent(gate_stream_t* stream, char const* spaces, gate_uint16_t* indent_stack, gate_size_t indent_index)
1535 {
1536 gate_string_t sp;
1537 gate_string_create_static_len(&sp, spaces, indent_stack[indent_index]);
1538 return gate_stream_print_string(stream, &sp);
1539 }
1540 */
1541
1542 static gate_result_t gate_yaml_print_inline(gate_property_t const* prop, gate_stream_t* stream)
1543 {
1544 gate_result_t ret = GATE_RESULT_FAILED;
1545 gate_uint32_t prop_type = gate_property_get_type(prop);
1546 switch (prop_type)
1547 {
1548 case GATE_PROPERTY_TYPE_EMPTY:
1549 {
1550 break;
1551 }
1552 case GATE_PROPERTY_TYPE_BOOL:
1553 {
1554 ret = gate_yaml_print_bool(stream, prop->data.bool_value);
1555 break;
1556 }
1557 case GATE_PROPERTY_TYPE_INT:
1558 {
1559 ret = gate_yaml_print_int(stream, prop->data.int_value);
1560 break;
1561 }
1562 case GATE_PROPERTY_TYPE_REAL:
1563 {
1564 ret = gate_yaml_print_real(stream, prop->data.real_value);
1565 break;
1566 }
1567 case GATE_PROPERTY_TYPE_STRING:
1568 {
1569 ret = gate_yaml_print_string(stream, &prop->data.string_value);
1570 break;
1571 }
1572 case GATE_PROPERTY_TYPE_ARRAY:
1573 {
1574 ret = gate_yaml_print_array(stream, prop->data.array_value);
1575 break;
1576 }
1577 case GATE_PROPERTY_TYPE_OBJECT:
1578 {
1579 ret = gate_yaml_print_object(stream, prop);
1580 break;
1581 }
1582 default:
1583 {
1584 ret = GATE_RESULT_NOTSUPPORTED;
1585 break;
1586 }
1587 }
1588 return ret;
1589 }
1590
1591 static gate_result_t gate_yaml_print_spaces(gate_size_t count, gate_stream_t* stream)
1592 {
1593 static gate_string_t const spaces = GATE_STRING_INIT_STATIC(" ");
1594 gate_result_t ret = GATE_RESULT_OK;
1595 gate_size_t len;
1596 gate_size_t lenwritten;
1597 while (count > 0)
1598 {
1599 len = (count > spaces.length) ? spaces.length : count;
1600 ret = gate_stream_write_block(stream, spaces.str, len, &lenwritten);
1601 GATE_BREAK_IF_FAILED(ret);
1602 count -= lenwritten;
1603 }
1604 return ret;
1605 }
1606
1607 static gate_result_t gate_yaml_print_indention(gate_stream_t* stream, gate_size_t spaces, gate_bool_t array_indicator)
1608 {
1609 gate_result_t ret;
1610 do
1611 {
1612 if ((spaces < 2) && array_indicator)
1613 {
1614 ret = GATE_RESULT_INVALIDSTATE;
1615 break;
1616 }
1617 if (array_indicator)
1618 {
1619 ret = gate_yaml_print_spaces(spaces - 2, stream);
1620 GATE_BREAK_IF_FAILED(ret);
1621 ret = gate_stream_print_cstr(stream, "- ");
1622 GATE_BREAK_IF_FAILED(ret);
1623 }
1624 else
1625 {
1626 ret = gate_yaml_print_spaces(spaces, stream);
1627 GATE_BREAK_IF_FAILED(ret);
1628 }
1629 } while (0);
1630 return ret;
1631 }
1632
1633 static gate_result_t gate_yaml_print(gate_property_t const* prop, gate_stream_t* stream, gate_size_t spaces, gate_bool_t array_indicator)
1634 {
1635 gate_result_t ret = GATE_RESULT_FAILED;
1636 gate_uint32_t prop_type = gate_property_get_type(prop);
1637 gate_property_t const* ptr_item;
1638 gate_size_t count, index;
1639 gate_array_t names = GATE_INIT_EMPTY;
1640 gate_string_t const* ptr_name;
1641
1642 do
1643 {
1644 switch (prop_type)
1645 {
1646 case GATE_PROPERTY_TYPE_BOOL:
1647 {
1648 ret = gate_yaml_print_indention(stream, spaces, array_indicator);
1649 GATE_BREAK_IF_FAILED(ret);
1650 ret = gate_yaml_print_bool(stream, prop->data.bool_value);
1651 GATE_BREAK_IF_FAILED(ret);
1652 ret = gate_stream_println_cstr(stream, NULL);
1653 break;
1654 }
1655 case GATE_PROPERTY_TYPE_INT:
1656 {
1657 ret = gate_yaml_print_indention(stream, spaces, array_indicator);
1658 GATE_BREAK_IF_FAILED(ret);
1659 ret = gate_yaml_print_int(stream, prop->data.int_value);
1660 GATE_BREAK_IF_FAILED(ret);
1661 ret = gate_stream_println_cstr(stream, NULL);
1662 break;
1663 }
1664 case GATE_PROPERTY_TYPE_REAL:
1665 {
1666 ret = gate_yaml_print_indention(stream, spaces, array_indicator);
1667 GATE_BREAK_IF_FAILED(ret);
1668 ret = gate_yaml_print_real(stream, prop->data.real_value);
1669 GATE_BREAK_IF_FAILED(ret);
1670 ret = gate_stream_println_cstr(stream, NULL);
1671 break;
1672 }
1673 case GATE_PROPERTY_TYPE_STRING:
1674 {
1675 ret = gate_yaml_print_indention(stream, spaces, array_indicator);
1676 GATE_BREAK_IF_FAILED(ret);
1677 ret = gate_yaml_print_string(stream, &prop->data.string_value);
1678 GATE_BREAK_IF_FAILED(ret);
1679 ret = gate_stream_println_cstr(stream, NULL);
1680 break;
1681 }
1682 case GATE_PROPERTY_TYPE_ARRAY:
1683 {
1684 count = gate_property_array_length(prop);
1685 if (count == 0)
1686 {
1687 ret = gate_yaml_print_indention(stream, spaces, array_indicator);
1688 GATE_BREAK_IF_FAILED(ret);
1689 ret = gate_stream_println_cstr(stream, "[]");
1690 }
1691 else
1692 {
1693 for (index = 0; index != count; ++index)
1694 {
1695 ptr_item = gate_property_array_get(prop, index);
1696 if (ptr_item)
1697 {
1698 ret = gate_yaml_print(ptr_item, stream, spaces + 2, true);
1699 GATE_BREAK_IF_FAILED(ret);
1700 }
1701 }
1702 }
1703 break;
1704 }
1705 case GATE_PROPERTY_TYPE_OBJECT:
1706 {
1707 if (NULL == gate_property_member_names(prop, &names))
1708 {
1709 ret = GATE_RESULT_FAILED;
1710 break;
1711 }
1712 count = gate_array_length(&names);
1713 if (count == 0)
1714 {
1715 ret = gate_yaml_print_indention(stream, spaces, array_indicator);
1716 GATE_BREAK_IF_FAILED(ret);
1717 ret = gate_stream_print_cstr(stream, "{}");
1718 GATE_BREAK_IF_FAILED(ret);
1719 }
1720 else
1721 {
1722 for (index = 0; index != count; ++index)
1723 {
1724 ptr_name = (gate_string_t const*)gate_array_get(&names, index);
1725 if (!ptr_name)
1726 {
1727 continue;
1728 }
1729 ptr_item = gate_property_member_get(prop, ptr_name);
1730 if (!ptr_item)
1731 {
1732 continue;
1733 }
1734 ret = gate_yaml_print_indention(stream, spaces, array_indicator);
1735 GATE_BREAK_IF_FAILED(ret);
1736 ret = gate_yaml_print_string(stream, ptr_name);
1737 GATE_BREAK_IF_FAILED(ret);
1738 ret = gate_stream_print_cstr(stream, ": ");
1739 GATE_BREAK_IF_FAILED(ret);
1740 switch (gate_property_get_type(ptr_item))
1741 {
1742 case GATE_PROPERTY_TYPE_BOOL:
1743 case GATE_PROPERTY_TYPE_INT:
1744 case GATE_PROPERTY_TYPE_REAL:
1745 case GATE_PROPERTY_TYPE_STRING:
1746 {
1747 ret = gate_yaml_print_inline(ptr_item, stream);
1748 GATE_BREAK_IF_FAILED(ret);
1749 ret = gate_stream_println_cstr(stream, NULL);
1750 break;
1751 }
1752 case GATE_PROPERTY_TYPE_ARRAY:
1753 {
1754 if (gate_property_array_length(ptr_item) == 0)
1755 {
1756 ret = gate_yaml_print_inline(ptr_item, stream);
1757 GATE_BREAK_IF_FAILED(ret);
1758 ret = gate_stream_println_cstr(stream, NULL);
1759 }
1760 else
1761 {
1762 ret = gate_stream_println_cstr(stream, NULL);
1763 GATE_BREAK_IF_FAILED(ret);
1764 ret = gate_yaml_print(ptr_item, stream, spaces + 2, false);
1765 }
1766 break;
1767 }
1768 case GATE_PROPERTY_TYPE_OBJECT:
1769 {
1770 if (gate_property_member_count(ptr_item) == 0)
1771 {
1772 ret = gate_yaml_print_inline(ptr_item, stream);
1773 GATE_BREAK_IF_FAILED(ret);
1774 ret = gate_stream_println_cstr(stream, NULL);
1775 }
1776 else
1777 {
1778 ret = gate_stream_println_cstr(stream, NULL);
1779 GATE_BREAK_IF_FAILED(ret);
1780 ret = gate_yaml_print(ptr_item, stream, spaces + 2, false);
1781 }
1782 break;
1783 }
1784 }
1785 GATE_BREAK_IF_FAILED(ret);
1786 array_indicator = false;
1787 } /* for() */
1788 }
1789 break;
1790 }
1791 case GATE_PROPERTY_TYPE_EMPTY:
1792 default:
1793 {
1794 ret = GATE_RESULT_NOTSUPPORTED;
1795 break;
1796 }
1797 }
1798 } while (0);
1799
1800 gate_array_release(&names);
1801
1802 return ret;
1803 }
1804
1805 gate_result_t gate_yaml_build(gate_property_t const* prop, gate_stream_t* dest_builder)
1806 {
1807 gate_result_t ret = GATE_RESULT_NOTIMPLEMENTED;
1808
1809 char spaces[4096 * 2];
1810 gate_uint16_t indent_stack[1024];
1811
1812 gate_mem_fill(spaces, ' ', sizeof(spaces));
1813 gate_mem_clear(indent_stack, sizeof(indent_stack));
1814
1815 ret = gate_yaml_print(prop, dest_builder, 0, false);
1816
1817 return ret;
1818 }
1819
1820 gate_result_t gate_yaml_build_string(gate_property_t const* prop, gate_string_t* output)
1821 {
1822 gate_result_t ret = GATE_RESULT_FAILED;
1823 gate_stringstream_t* str_stream = NULL;
1824
1825 do
1826 {
1827 str_stream = gate_stringstream_create(256);
1828 if (str_stream == NULL)
1829 {
1830 ret = GATE_RESULT_OUTOFMEMORY;
1831 break;
1832 }
1833
1834 ret = gate_yaml_build(prop, (gate_stream_t*)str_stream);
1835 GATE_BREAK_IF_FAILED(ret);
1836
1837 ret = gate_stringstream_to_string(str_stream, output);
1838 } while (0);
1839
1840 if (str_stream)
1841 {
1842 gate_object_release(str_stream);
1843 }
1844 return ret;
1845 }
1846