summaryrefslogtreecommitdiff
path: root/src/adb_walk_text.c
blob: 972df4f0870e59fd12be5558faeb8cf118fa0902 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <errno.h>
#include "adb.h"

//#define DEBUG_PRINT
#ifdef DEBUG_PRINT
#include <stdio.h>
#define dbg_printf(args...) fprintf(stderr, args)
#else
#define dbg_printf(args...)
#endif

int adb_walk_text(struct adb_walk *d, struct apk_istream *is)
{
	const apk_blob_t token = APK_BLOB_STR("\n");
	const apk_blob_t comment = APK_BLOB_STR(" #");
	const apk_blob_t key_sep = APK_BLOB_STR(": ");
	char mblockdata[1024*4];
	apk_blob_t l, comm, mblock = APK_BLOB_BUF(mblockdata);
	int r = 0, i, multi_line = 0, nesting = 0, new_item = 0;
	uint8_t started[64] = {0};

	if (IS_ERR_OR_NULL(is)) return PTR_ERR(is);
	if (apk_istream_get_delim(is, token, &l) != 0) goto err;
	apk_blob_pull_blob_match(&l, APK_BLOB_STR("#%SCHEMA: "));
	if ((r = d->ops->schema(d, apk_blob_pull_uint(&l, 16))) != 0) goto err;

	started[0] = 1;
	while (apk_istream_get_delim(is, token, &l) == 0) {
		for (i = 0; l.len >= 2 && l.ptr[0] == ' ' && l.ptr[1] == ' '; i++, l.ptr += 2, l.len -= 2)
			if (multi_line && i >= multi_line) break;

		for (; nesting > i; nesting--) {
			if (multi_line) {
				apk_blob_t data = apk_blob_pushed(APK_BLOB_BUF(mblockdata), mblock);
				if (APK_BLOB_IS_NULL(data)) {
					r = -E2BIG;
					goto err;
				}
				if (data.len && data.ptr[data.len-1] == '\n') data.len--;
				dbg_printf("Multiline-Scalar >%d> "BLOB_FMT"\n", nesting, BLOB_PRINTF(data));
				if ((r = d->ops->scalar(d, data, 1)) != 0) goto err;
				mblock = APK_BLOB_BUF(mblockdata);
				multi_line = 0;
			}
			if (started[nesting]) {
				dbg_printf("End %d\n", nesting);
				if ((r = d->ops->end(d)) != 0) goto err;
			}
		}
		if (l.ptr[0] == '-' && l.ptr[1] == ' ') {
			l.ptr += 2, l.len -= 2;
			if (!started[nesting]) {
				dbg_printf("Array %d\n", nesting);
				if ((r = d->ops->start_array(d, 0)) != 0) goto err;
				started[nesting] = 1;
			}
			new_item = 1;
		}
		dbg_printf(" >%d/%d< >"BLOB_FMT"<\n", nesting, i, BLOB_PRINTF(l));

		if (multi_line) {
			dbg_printf("Scalar-Block:>%d> "BLOB_FMT"\n", nesting, BLOB_PRINTF(l));
			apk_blob_push_blob(&mblock, l);
			apk_blob_push_blob(&mblock, APK_BLOB_STR("\n"));
			new_item = 0;
			continue;
		}

		if (l.ptr[0] == '#') {
			if ((r = d->ops->comment(d, l)) != 0) goto err;
			continue;
		}

		// contains ' #' -> comment
		if (!apk_blob_split(l, comment, &l, &comm))
			comm.len = 0;

		if (l.len) {
			apk_blob_t key = APK_BLOB_NULL, scalar = APK_BLOB_NULL;
			int start = 0;

			if (apk_blob_split(l, key_sep, &key, &scalar)) {
				// contains ': ' -> key + scalar
			} else if (l.ptr[l.len-1] == ':') {
				// ends ':' -> key + indented object/array
				key = APK_BLOB_PTR_LEN(l.ptr, l.len-1);
				start = 1;
			} else {
				scalar = l;
			}
			if (key.len) {
				if (new_item) {
					started[++nesting] = 0;
					dbg_printf("Array-Object %d\n", nesting);
				}
				if (!started[nesting]) {
					dbg_printf("Object %d\n", nesting);
					if ((r = d->ops->start_object(d)) != 0) goto err;
					started[nesting] = 1;
				}
				dbg_printf("Key >%d> "BLOB_FMT"\n", nesting, BLOB_PRINTF(key));
				if ((r = d->ops->key(d, key)) != 0) goto err;
				if (start) started[++nesting] = 0;
			}

			if (scalar.len) {
				if (scalar.ptr[0] == '|') {
					dbg_printf("Scalar-block >%d> "BLOB_FMT"\n", nesting, BLOB_PRINTF(scalar));
					// scalar '|' -> starts string literal block
					started[++nesting] = 0;
					multi_line = nesting;
				} else {
					dbg_printf("Scalar >%d> "BLOB_FMT"\n", nesting, BLOB_PRINTF(scalar));
					if ((r = d->ops->scalar(d, scalar, 0)) != 0) goto err;
				}
			}
			new_item = 0;
		}

		if (comm.len) {
			if ((r = d->ops->comment(d, comm)) != 0) goto err;
		}

		//fprintf(stderr, ">%d> "BLOB_FMT"\n", indent, BLOB_PRINTF(l));
	}
	d->ops->end(d);

err:
	if (r) apk_istream_error(is, r);
	return apk_istream_close(is);
}