c0data 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +143 -0
- data/c0-c/c0.h +3926 -0
- data/ext/c0/c0_ext.c +1061 -0
- data/ext/c0/extconf.rb +7 -0
- data/lib/c0/body.rb +314 -0
- data/lib/c0/builder.rb +64 -0
- data/lib/c0/document.rb +243 -0
- data/lib/c0/pretty.rb +28 -0
- data/lib/c0/stream.rb +76 -0
- data/lib/c0.rb +123 -0
- metadata +53 -0
data/c0-c/c0.h
ADDED
|
@@ -0,0 +1,3926 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* c0.h — C0DATA 1.0: structured data using ASCII C0 control codes.
|
|
3
|
+
*
|
|
4
|
+
* Single-header library. In exactly one translation unit:
|
|
5
|
+
*
|
|
6
|
+
* #define C0_IMPLEMENTATION
|
|
7
|
+
* #include "c0.h"
|
|
8
|
+
*
|
|
9
|
+
* Elsewhere just #include "c0.h" for the declarations.
|
|
10
|
+
*
|
|
11
|
+
* The read path is zero-copy and allocation-free: views and cursors walk a
|
|
12
|
+
* caller-owned buffer and hand back (pointer, length) slices into it. The
|
|
13
|
+
* hot loop outside text is a single comparison, `byte < 0x20`.
|
|
14
|
+
*
|
|
15
|
+
* Reading is a two-step contract: `c0_validate` checks a buffer against the
|
|
16
|
+
* grammar once; the navigators (`c0_body`, `c0_document`, the iterators) then
|
|
17
|
+
* assume well-formed input and stop early, setting their `err`, when they
|
|
18
|
+
* meet something malformed. Writing is `c0_builder`, which owns a growable
|
|
19
|
+
* buffer and spells every value in its one canonical form.
|
|
20
|
+
*
|
|
21
|
+
* Spec: https://github.com/c0data/c0-spec (DESIGN.md, GRAMMAR.md, DIFF.md).
|
|
22
|
+
* License: MIT.
|
|
23
|
+
*/
|
|
24
|
+
#ifndef C0_H
|
|
25
|
+
#define C0_H
|
|
26
|
+
|
|
27
|
+
#include <stddef.h>
|
|
28
|
+
#include <stdint.h>
|
|
29
|
+
|
|
30
|
+
#ifdef __cplusplus
|
|
31
|
+
extern "C" {
|
|
32
|
+
#endif
|
|
33
|
+
|
|
34
|
+
#define C0_VERSION "0.2.0"
|
|
35
|
+
|
|
36
|
+
/* ---- Assigned control codes (DESIGN.md "Assigned Control Codes") ------ */
|
|
37
|
+
#define C0_SOH 0x01 /**< Heading: a key for the value after it; SOH RS a header row */
|
|
38
|
+
#define C0_STX 0x02 /**< Start of text (a literal value) */
|
|
39
|
+
#define C0_ETX 0x03 /**< End of text */
|
|
40
|
+
#define C0_EOT 0x04 /**< End of document */
|
|
41
|
+
#define C0_ENQ 0x05 /**< Reference */
|
|
42
|
+
#define C0_BEL 0x07 /**< Comment: a note, or marks the next element dead */
|
|
43
|
+
#define C0_DLE 0x10 /**< Recurse (opens a nested level) outside text; escape inside */
|
|
44
|
+
#define C0_SYN 0x16 /**< Start of document, then the heading */
|
|
45
|
+
#define C0_ETB 0x17 /**< Commit marker (stream mode) */
|
|
46
|
+
#define C0_CAN 0x18 /**< Close a nested level */
|
|
47
|
+
#define C0_EM 0x19 /**< End of medium: a raw blob follows */
|
|
48
|
+
#define C0_SUB 0x1a /**< Substitution (C0-DIFF only) */
|
|
49
|
+
#define C0_FS 0x1c /**< File separator */
|
|
50
|
+
#define C0_GS 0x1d /**< Group separator */
|
|
51
|
+
#define C0_RS 0x1e /**< Record separator */
|
|
52
|
+
#define C0_US 0x1f /**< Unit separator */
|
|
53
|
+
|
|
54
|
+
/* Layout codes outside text. */
|
|
55
|
+
#define C0_HT 0x09
|
|
56
|
+
#define C0_LF 0x0a
|
|
57
|
+
#define C0_CR 0x0d
|
|
58
|
+
#define C0_SP 0x20
|
|
59
|
+
|
|
60
|
+
/** A zero-copy view into a buffer: not NUL-terminated. */
|
|
61
|
+
typedef struct {
|
|
62
|
+
const uint8_t *ptr;
|
|
63
|
+
size_t len;
|
|
64
|
+
} c0_bytes;
|
|
65
|
+
|
|
66
|
+
/** Status codes shared by every part of the library. */
|
|
67
|
+
typedef enum {
|
|
68
|
+
C0_OK = 0,
|
|
69
|
+
C0_ERR_SYNTAX, /**< malformed input (see c0_error.msg / .pos) */
|
|
70
|
+
C0_ERR_UNASSIGNED, /**< an unassigned control code outside text */
|
|
71
|
+
C0_ERR_UNEXPECTED_END, /**< input ended inside text, a level, or after DLE/BEL */
|
|
72
|
+
C0_ERR_TOO_DEEP, /**< nesting deeper than the validator's limit */
|
|
73
|
+
C0_ERR_NOT_SCALAR, /**< c0_value on a level or reference */
|
|
74
|
+
C0_ERR_NOT_LEVEL, /**< c0_unit_level on a unit that is not DLE … CAN */
|
|
75
|
+
C0_ERR_NOT_LIST, /**< c0_unit_list on a level that is not a list */
|
|
76
|
+
C0_ERR_NOT_REFERENCE, /**< c0_unit_ref on a unit that is not ENQ … */
|
|
77
|
+
C0_ERR_NEEDS_DECODE, /**< c0_value without scratch on text with escapes */
|
|
78
|
+
C0_ERR_NOT_FOUND, /**< no such group / key / field / blob */
|
|
79
|
+
C0_ERR_BAD_NAME, /**< builder: a name with a control byte or edge space */
|
|
80
|
+
C0_ERR_BAD_STATE, /**< builder: e.g. a level closed that was never opened */
|
|
81
|
+
C0_ERR_OOM /**< allocation failed */
|
|
82
|
+
} c0_status;
|
|
83
|
+
|
|
84
|
+
/** Error detail: the code, the byte offset it was found at, and a static
|
|
85
|
+
* message. Every reader accepts a NULL `c0_error *` when it does not want it. */
|
|
86
|
+
typedef struct {
|
|
87
|
+
c0_status code;
|
|
88
|
+
size_t pos;
|
|
89
|
+
const char *msg;
|
|
90
|
+
} c0_error;
|
|
91
|
+
|
|
92
|
+
/** Warnings a document reader raises (DESIGN.md "Warnings"), as bits. */
|
|
93
|
+
#define C0_WARN_NO_SYN 0x01u /**< loud: no SYN at the start */
|
|
94
|
+
#define C0_WARN_NO_EOT 0x02u /**< loud: no EOT and the shape is not stream */
|
|
95
|
+
#define C0_WARN_UNKNOWN_KEY 0x04u /**< loud: a heading key the reader does not know */
|
|
96
|
+
#define C0_WARN_DUPLICATE_KEY 0x08u /**< a duplicate heading key (last wins) */
|
|
97
|
+
|
|
98
|
+
/* ---- Bytewise helpers -------------------------------------------------- */
|
|
99
|
+
|
|
100
|
+
/** Whether a byte is one of the sixteen assigned codes. */
|
|
101
|
+
int c0_is_assigned(uint8_t byte);
|
|
102
|
+
/** Whether a byte is layout outside text (SP, HT, LF, CR). */
|
|
103
|
+
int c0_is_layout(uint8_t byte);
|
|
104
|
+
/** Whether a byte is one of the seven codes that take a DLE inside text
|
|
105
|
+
* (STX ETX DLE ETB EOT SYN EM). */
|
|
106
|
+
int c0_is_text_escaped(uint8_t byte);
|
|
107
|
+
/** Whether a value can be written plain: no byte below 0x20, no edge whitespace. */
|
|
108
|
+
int c0_is_plain(const uint8_t *p, size_t len);
|
|
109
|
+
/** Whether bytes are a legal name (label, key, header name): same test as plain. */
|
|
110
|
+
int c0_is_name(const uint8_t *p, size_t len);
|
|
111
|
+
/** A view over `len` bytes at `ptr`. */
|
|
112
|
+
c0_bytes c0_bytes_of(const uint8_t *ptr, size_t len);
|
|
113
|
+
/** Layout trimmed from both ends of a raw slice. */
|
|
114
|
+
c0_bytes c0_trim(c0_bytes raw);
|
|
115
|
+
|
|
116
|
+
/** Decode the inside of a text (the bytes between STX and ETX): DLE escapes
|
|
117
|
+
* removed. `out` needs room for `len` bytes (decoding never grows). Returns
|
|
118
|
+
* the logical length. */
|
|
119
|
+
size_t c0_decode_text(const uint8_t *inside, size_t len, uint8_t *out);
|
|
120
|
+
|
|
121
|
+
/* ---- Scanning primitives (c0.cr `Scan`) --------------------------------
|
|
122
|
+
* Each takes a buffer and a position and returns the position after the
|
|
123
|
+
* construct; on malformed input it records the error (when err is given)
|
|
124
|
+
* and returns `stop`. Converters and bindings build on these. */
|
|
125
|
+
|
|
126
|
+
/** Skip layout bytes. */
|
|
127
|
+
size_t c0_scan_layout(const uint8_t *buf, size_t pos, size_t stop);
|
|
128
|
+
/** Skip a run of bytes >= 0x20 (a label, key, payload or plain value with its layout). */
|
|
129
|
+
size_t c0_scan_plain(const uint8_t *buf, size_t pos, size_t stop);
|
|
130
|
+
/** At an STX: the position after the matching ETX. */
|
|
131
|
+
size_t c0_scan_text(const uint8_t *buf, size_t pos, size_t stop, c0_error *err);
|
|
132
|
+
/** At a DLE: the position after the matching CAN. */
|
|
133
|
+
size_t c0_scan_level(const uint8_t *buf, size_t pos, size_t stop, c0_error *err);
|
|
134
|
+
/** From just after a US (or a key's US): the end of the unit's value. */
|
|
135
|
+
size_t c0_scan_unit(const uint8_t *buf, size_t pos, size_t stop, c0_error *err);
|
|
136
|
+
/** At a BEL: past the note, or past the whole dead element. */
|
|
137
|
+
size_t c0_scan_comment(const uint8_t *buf, size_t pos, size_t stop, c0_error *err);
|
|
138
|
+
/** From just after an RS: the end of the record. */
|
|
139
|
+
size_t c0_scan_record_end(const uint8_t *buf, size_t pos, size_t stop, c0_error *err);
|
|
140
|
+
|
|
141
|
+
/* ---- Validation -------------------------------------------------------- */
|
|
142
|
+
|
|
143
|
+
/** Check a whole buffer (one or more documents, an optional EM tail) against
|
|
144
|
+
* GRAMMAR.md. Returns C0_OK or the first error (detail in *err when given). */
|
|
145
|
+
c0_status c0_validate(const uint8_t *buf, size_t len, c0_error *err);
|
|
146
|
+
|
|
147
|
+
/** Whether bytes are a canonical unit (DESIGN.md "Canonical Form"):
|
|
148
|
+
* well-formed, no layout, one spelling per value, unique keys in each keyed
|
|
149
|
+
* record, no framing, no comments. */
|
|
150
|
+
int c0_canonical(const uint8_t *buf, size_t len);
|
|
151
|
+
|
|
152
|
+
/* ---- Units and values -------------------------------------------------- */
|
|
153
|
+
|
|
154
|
+
/** What a raw unit holds, by its first non-layout byte. */
|
|
155
|
+
typedef enum {
|
|
156
|
+
C0_UNIT_PLAIN = 0, /**< a plain value, the empty value, or a unit holding only a note */
|
|
157
|
+
C0_UNIT_TEXT, /**< STX … ETX */
|
|
158
|
+
C0_UNIT_LEVEL, /**< DLE … CAN */
|
|
159
|
+
C0_UNIT_REF /**< ENQ name, or ENQ STX path ETX */
|
|
160
|
+
} c0_unit_kind;
|
|
161
|
+
|
|
162
|
+
c0_unit_kind c0_unit_type(c0_bytes raw);
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The logical value of a raw unit (as yielded by c0_next_unit, c0_next_pair,
|
|
166
|
+
* c0_record_field …): for a plain value the slice with edge layout removed;
|
|
167
|
+
* for text the inside with its escapes decoded; for a unit holding only a
|
|
168
|
+
* note, the empty value. *out points into the buffer when no decoding is
|
|
169
|
+
* needed; when the text holds DLE escapes it is decoded into `scratch`
|
|
170
|
+
* (room for raw.len bytes always suffices) and *out points there. With a
|
|
171
|
+
* NULL scratch such a unit answers C0_ERR_NEEDS_DECODE. A level or reference
|
|
172
|
+
* answers C0_ERR_NOT_SCALAR; malformed input C0_ERR_SYNTAX.
|
|
173
|
+
*/
|
|
174
|
+
c0_status c0_value(c0_bytes raw, uint8_t *scratch, c0_bytes *out);
|
|
175
|
+
|
|
176
|
+
/** A body: a document body or the inside of a nested level, as a range of
|
|
177
|
+
* the buffer. Build one with c0_body_view, c0_document_read or c0_unit_level. */
|
|
178
|
+
typedef struct {
|
|
179
|
+
const uint8_t *buf;
|
|
180
|
+
size_t start;
|
|
181
|
+
size_t stop;
|
|
182
|
+
} c0_body;
|
|
183
|
+
|
|
184
|
+
/** A body over a whole buffer (a slice with no SYN: records, groups, files). */
|
|
185
|
+
c0_body c0_body_view(const uint8_t *buf, size_t len);
|
|
186
|
+
|
|
187
|
+
/** A raw unit as a nested level: the body between its DLE and CAN. */
|
|
188
|
+
c0_status c0_unit_level(c0_bytes raw, c0_body *out);
|
|
189
|
+
|
|
190
|
+
/** A parsed reference: one segment (a group) or two or three (group,
|
|
191
|
+
* record id, field name). Segments are slices into the buffer. */
|
|
192
|
+
typedef struct {
|
|
193
|
+
c0_bytes seg[3];
|
|
194
|
+
size_t count;
|
|
195
|
+
} c0_ref;
|
|
196
|
+
|
|
197
|
+
/** A raw unit as a reference. Checks the form only (DESIGN.md "References"). */
|
|
198
|
+
c0_status c0_unit_ref(c0_bytes raw, c0_ref *out);
|
|
199
|
+
|
|
200
|
+
/* ---- Records ------------------------------------------------------------ */
|
|
201
|
+
|
|
202
|
+
/** A record: the bytes after its RS, trailing layout excluded. Positional
|
|
203
|
+
* (units separated by US) or keyed (SOH key US value …), never mixed. */
|
|
204
|
+
typedef struct {
|
|
205
|
+
const uint8_t *buf;
|
|
206
|
+
size_t start;
|
|
207
|
+
size_t stop;
|
|
208
|
+
} c0_record;
|
|
209
|
+
|
|
210
|
+
/** Whether the record is keyed (its first live unit is an SOH). */
|
|
211
|
+
int c0_record_keyed(c0_record r);
|
|
212
|
+
|
|
213
|
+
/** Cursor over the positional units of a record, or the items of a list. */
|
|
214
|
+
typedef struct {
|
|
215
|
+
const uint8_t *buf;
|
|
216
|
+
size_t pos;
|
|
217
|
+
size_t stop;
|
|
218
|
+
int done;
|
|
219
|
+
c0_error err;
|
|
220
|
+
} c0_unit_iter;
|
|
221
|
+
|
|
222
|
+
/** Positional units, in order. Each `raw` is the unit's bytes with its
|
|
223
|
+
* layout; pass it to c0_value / c0_unit_level / c0_unit_ref. N separators
|
|
224
|
+
* yield N+1 units; an empty record yields one empty unit. A keyed record
|
|
225
|
+
* yields nothing. */
|
|
226
|
+
c0_unit_iter c0_record_units(c0_record r);
|
|
227
|
+
int c0_next_unit(c0_unit_iter *it, c0_bytes *raw);
|
|
228
|
+
|
|
229
|
+
/** Number of positional units. */
|
|
230
|
+
size_t c0_record_count(c0_record r);
|
|
231
|
+
/** Raw unit n (0-based), or C0_ERR_NOT_FOUND. */
|
|
232
|
+
c0_status c0_record_field(c0_record r, size_t n, c0_bytes *raw);
|
|
233
|
+
|
|
234
|
+
/** Cursor over keyed units (a keyed record, or a document heading). */
|
|
235
|
+
typedef struct {
|
|
236
|
+
const uint8_t *buf;
|
|
237
|
+
size_t pos;
|
|
238
|
+
size_t stop;
|
|
239
|
+
c0_error err;
|
|
240
|
+
} c0_pair_iter;
|
|
241
|
+
|
|
242
|
+
/** Keyed units in written order, duplicates included, dead units skipped.
|
|
243
|
+
* `key` is the name; `raw` the unit's raw value. */
|
|
244
|
+
c0_pair_iter c0_record_pairs(c0_record r);
|
|
245
|
+
int c0_next_pair(c0_pair_iter *it, c0_bytes *key, c0_bytes *raw);
|
|
246
|
+
|
|
247
|
+
/** Raw value for a key (last wins), or C0_ERR_NOT_FOUND. */
|
|
248
|
+
c0_status c0_record_get(c0_record r, const uint8_t *key, size_t klen, c0_bytes *raw);
|
|
249
|
+
c0_status c0_record_get_str(c0_record r, const char *key, c0_bytes *raw);
|
|
250
|
+
|
|
251
|
+
/** A raw unit as a flat list: a level holding at most one positional record.
|
|
252
|
+
* On C0_OK `items` iterates the items (none for the empty level). A level
|
|
253
|
+
* with several records, a keyed record, groups or files is C0_ERR_NOT_LIST. */
|
|
254
|
+
c0_status c0_unit_list(c0_bytes raw, c0_unit_iter *items);
|
|
255
|
+
|
|
256
|
+
/* ---- Bodies: files, groups, records ------------------------------------ */
|
|
257
|
+
|
|
258
|
+
/** A file within a body: its name and the range of its contents. */
|
|
259
|
+
typedef struct {
|
|
260
|
+
const uint8_t *buf;
|
|
261
|
+
size_t name_start;
|
|
262
|
+
size_t name_end;
|
|
263
|
+
size_t start;
|
|
264
|
+
size_t stop;
|
|
265
|
+
} c0_file;
|
|
266
|
+
|
|
267
|
+
/** A group: its depth (number of GS; 0 for the unnamed group at the start of
|
|
268
|
+
* a file or level), name, and the range of its contents (header row and
|
|
269
|
+
* records; deeper GS-repeated sections are separate groups). */
|
|
270
|
+
typedef struct {
|
|
271
|
+
const uint8_t *buf;
|
|
272
|
+
size_t depth;
|
|
273
|
+
size_t name_start;
|
|
274
|
+
size_t name_end;
|
|
275
|
+
size_t start;
|
|
276
|
+
size_t stop;
|
|
277
|
+
} c0_group;
|
|
278
|
+
|
|
279
|
+
c0_bytes c0_file_name(c0_file f);
|
|
280
|
+
c0_bytes c0_group_name(c0_group g);
|
|
281
|
+
|
|
282
|
+
/** Cursor over the files of a body. Records and groups before the first FS
|
|
283
|
+
* form an unnamed file, reported only when it has content or no FS follows.
|
|
284
|
+
* A dead FS line is skipped: what follows it stays in the previous file. */
|
|
285
|
+
typedef struct {
|
|
286
|
+
const uint8_t *buf;
|
|
287
|
+
size_t pos;
|
|
288
|
+
size_t stop;
|
|
289
|
+
int first;
|
|
290
|
+
int done;
|
|
291
|
+
c0_error err;
|
|
292
|
+
} c0_file_iter;
|
|
293
|
+
|
|
294
|
+
c0_file_iter c0_body_files(c0_body b);
|
|
295
|
+
int c0_next_file(c0_file_iter *it, c0_file *out);
|
|
296
|
+
|
|
297
|
+
/** Cursor over groups: every GS line at any depth, in order, plus the
|
|
298
|
+
* unnamed leading group of a file or level when it holds a header row or
|
|
299
|
+
* records. A dead GS line is skipped: its records stay in the group before. */
|
|
300
|
+
typedef struct {
|
|
301
|
+
const uint8_t *buf;
|
|
302
|
+
size_t pos;
|
|
303
|
+
size_t stop;
|
|
304
|
+
int first;
|
|
305
|
+
int done;
|
|
306
|
+
c0_error err;
|
|
307
|
+
} c0_group_iter;
|
|
308
|
+
|
|
309
|
+
/** Groups of one file. */
|
|
310
|
+
c0_group_iter c0_file_groups(c0_file f);
|
|
311
|
+
/** Groups of every file of a body, in order (FS lines are skipped). */
|
|
312
|
+
c0_group_iter c0_body_groups(c0_body b);
|
|
313
|
+
int c0_next_group(c0_group_iter *it, c0_group *out);
|
|
314
|
+
|
|
315
|
+
/** First group with the given name in the body (any file), or C0_ERR_NOT_FOUND. */
|
|
316
|
+
c0_status c0_body_find_group(c0_body b, const uint8_t *name, size_t nlen, c0_group *out);
|
|
317
|
+
c0_status c0_body_find_group_str(c0_body b, const char *name, c0_group *out);
|
|
318
|
+
|
|
319
|
+
/** Whether the body is a bare record stream: one unnamed file, at most the
|
|
320
|
+
* unnamed group. */
|
|
321
|
+
int c0_body_bare(c0_body b);
|
|
322
|
+
|
|
323
|
+
/** The group's header row as a record (its units are the header names).
|
|
324
|
+
* Returns 0 when the group has none. */
|
|
325
|
+
int c0_group_header(c0_group g, c0_record *out);
|
|
326
|
+
|
|
327
|
+
/** Cursor over the live records of a group (header rows, notes, commits and
|
|
328
|
+
* dead elements skipped). */
|
|
329
|
+
typedef struct {
|
|
330
|
+
const uint8_t *buf;
|
|
331
|
+
size_t pos;
|
|
332
|
+
size_t stop;
|
|
333
|
+
int done;
|
|
334
|
+
c0_error err;
|
|
335
|
+
} c0_record_iter;
|
|
336
|
+
|
|
337
|
+
c0_record_iter c0_group_records(c0_group g);
|
|
338
|
+
/** Records of a bare body (the unnamed group's). */
|
|
339
|
+
c0_record_iter c0_body_records(c0_body b);
|
|
340
|
+
int c0_next_record(c0_record_iter *it, c0_record *out);
|
|
341
|
+
|
|
342
|
+
/** Index of a header name in the group's header row, or C0_ERR_NOT_FOUND. */
|
|
343
|
+
c0_status c0_group_column(c0_group g, const uint8_t *name, size_t nlen, size_t *index);
|
|
344
|
+
|
|
345
|
+
/* ---- Documents and containers ------------------------------------------ */
|
|
346
|
+
|
|
347
|
+
/** One document: an optional SYN heading, a body, an optional EOT. */
|
|
348
|
+
typedef struct {
|
|
349
|
+
const uint8_t *buf;
|
|
350
|
+
size_t start;
|
|
351
|
+
size_t stop; /**< after the EOT, or where the document ends */
|
|
352
|
+
int has_syn;
|
|
353
|
+
int has_eot;
|
|
354
|
+
size_t heading_start; /**< the keyed units after SYN … */
|
|
355
|
+
size_t heading_end; /**< … up to the first body code */
|
|
356
|
+
c0_body body;
|
|
357
|
+
unsigned warnings; /**< C0_WARN_* bits */
|
|
358
|
+
} c0_document;
|
|
359
|
+
|
|
360
|
+
/** Read the document starting at `start` (after any layout). Returns C0_OK or
|
|
361
|
+
* the first structural error met while finding the heading and the body's
|
|
362
|
+
* end. Shapes inside the body are not checked here: use c0_validate. */
|
|
363
|
+
c0_status c0_document_read(const uint8_t *buf, size_t len, size_t start, c0_document *out, c0_error *err);
|
|
364
|
+
|
|
365
|
+
/** The heading's keyed units, in written order (dead units skipped). */
|
|
366
|
+
c0_pair_iter c0_document_heading(const c0_document *d);
|
|
367
|
+
/** Raw heading value for a key (last wins), or C0_ERR_NOT_FOUND. */
|
|
368
|
+
c0_status c0_heading_get(const c0_document *d, const uint8_t *key, size_t klen, c0_bytes *raw);
|
|
369
|
+
c0_status c0_heading_get_str(const c0_document *d, const char *key, c0_bytes *raw);
|
|
370
|
+
/** The `shape` value (decoded, must be plain), or an empty slice when absent. */
|
|
371
|
+
c0_bytes c0_heading_shape(const c0_document *d);
|
|
372
|
+
/** The `version` value as an integer; 1 when absent or not a number. */
|
|
373
|
+
int c0_heading_version(const c0_document *d);
|
|
374
|
+
/** The `blob` table (a level) as a body, or C0_ERR_NOT_FOUND / C0_ERR_NOT_LEVEL. */
|
|
375
|
+
c0_status c0_heading_blob(const c0_document *d, c0_body *out);
|
|
376
|
+
|
|
377
|
+
/** Cursor over the documents of a container (a file or message): each begins
|
|
378
|
+
* with SYN except possibly the first; EM ends the scan and starts the tail. */
|
|
379
|
+
typedef struct {
|
|
380
|
+
const uint8_t *buf;
|
|
381
|
+
size_t len;
|
|
382
|
+
size_t pos;
|
|
383
|
+
int has_em;
|
|
384
|
+
size_t em_pos;
|
|
385
|
+
int done;
|
|
386
|
+
c0_error err;
|
|
387
|
+
} c0_doc_iter;
|
|
388
|
+
|
|
389
|
+
c0_doc_iter c0_documents(const uint8_t *buf, size_t len);
|
|
390
|
+
int c0_next_document(c0_doc_iter *it, c0_document *out);
|
|
391
|
+
/** After the cursor is exhausted: the raw bytes after EM (empty without EM). */
|
|
392
|
+
c0_bytes c0_doc_iter_tail(const c0_doc_iter *it);
|
|
393
|
+
|
|
394
|
+
/** Scan the whole container: *tail gets the raw region after EM (empty when
|
|
395
|
+
* there is none, *has_em says which). Returns the first error met. */
|
|
396
|
+
c0_status c0_container_tail(const uint8_t *buf, size_t len, c0_bytes *tail, int *has_em, c0_error *err);
|
|
397
|
+
|
|
398
|
+
/** The bytes of a named blob from the tail, located by the first document's
|
|
399
|
+
* `blob` table (columns matched by header name). C0_ERR_NOT_FOUND when there
|
|
400
|
+
* is no table, no such row, or no EM. */
|
|
401
|
+
c0_status c0_blob(const uint8_t *buf, size_t len, const uint8_t *name, size_t nlen, c0_bytes *out, c0_error *err);
|
|
402
|
+
c0_status c0_blob_str(const uint8_t *buf, size_t len, const char *name, c0_bytes *out, c0_error *err);
|
|
403
|
+
|
|
404
|
+
/* ---- Builder (writer) --------------------------------------------------- */
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Writes C0DATA in canonical compact form into a growable buffer it owns;
|
|
408
|
+
* call c0_builder_free when done. Once `status` is set, further writes are
|
|
409
|
+
* no-ops; check it before using the bytes. Values are byte-transparent and
|
|
410
|
+
* written in their one spelling: plain when they can be, text otherwise with
|
|
411
|
+
* only the seven codes escaped. Names (file, group, header, key, reference
|
|
412
|
+
* segment, ETB payload) must be plain, or C0_ERR_BAD_NAME is set.
|
|
413
|
+
*
|
|
414
|
+
* Two layers. The primitives, `c0_builder_<code>`, are one function per
|
|
415
|
+
* code (fs gs rs us sub soh dle can bel enq syn eot etb em); each writes its code
|
|
416
|
+
* and, when given, what grammatically follows it, so a sequence of calls
|
|
417
|
+
* reads like the bytes:
|
|
418
|
+
*
|
|
419
|
+
* c0_builder_rs_str(&b, "Alice"); c0_builder_us_str(&b, "1502.30"); ␞Alice␟1502.30
|
|
420
|
+
* c0_builder_rs(&b); c0_builder_soh_str(&b, "host"); c0_builder_us_str(&b, "localhost");
|
|
421
|
+
* c0_builder_bel(&b); c0_builder_gs_str(&b, "old"); a dead group
|
|
422
|
+
*
|
|
423
|
+
* The composed functions, `c0_build_<what>` (file, group, header, record,
|
|
424
|
+
* field, map, key, level, list, ref, note), are built on the primitives and
|
|
425
|
+
* cannot write ill-formed output. The primitives write what you spell;
|
|
426
|
+
* well-formedness is then yours to keep. Both layers share the record state,
|
|
427
|
+
* so they mix.
|
|
428
|
+
*
|
|
429
|
+
* A record is opened by c0_build_record or c0_builder_rs (or implicitly by
|
|
430
|
+
* the first c0_build_field / c0_build_key / c0_build_level_open /
|
|
431
|
+
* c0_build_list / c0_build_ref) and ended by the next structural call. Keyed
|
|
432
|
+
* units written right after c0_build_syn belong to the heading, not to a
|
|
433
|
+
* record.
|
|
434
|
+
*/
|
|
435
|
+
typedef struct {
|
|
436
|
+
uint8_t *data;
|
|
437
|
+
size_t len;
|
|
438
|
+
size_t cap;
|
|
439
|
+
c0_status status;
|
|
440
|
+
int state; /**< internal: top / heading / record */
|
|
441
|
+
size_t units; /**< internal: units written in the open record */
|
|
442
|
+
int *stack; /**< internal: saved state per open level */
|
|
443
|
+
size_t depth; /**< internal: open levels */
|
|
444
|
+
size_t stack_cap; /**< internal */
|
|
445
|
+
} c0_builder;
|
|
446
|
+
|
|
447
|
+
void c0_builder_init(c0_builder *b);
|
|
448
|
+
void c0_builder_free(c0_builder *b);
|
|
449
|
+
c0_status c0_builder_status(const c0_builder *b);
|
|
450
|
+
c0_bytes c0_builder_bytes(const c0_builder *b);
|
|
451
|
+
|
|
452
|
+
/* primitives: one function per code. The bare form writes the code alone;
|
|
453
|
+
* the `_name` / `_value` / `_text` / `_payload` form writes the code and what
|
|
454
|
+
* follows it; `_str` is that form on a NUL-terminated string. Values take
|
|
455
|
+
* their one spelling (plain or text); names must be plain. */
|
|
456
|
+
void c0_builder_fs(c0_builder *b, const uint8_t *name, size_t nlen); /**< FS name; ends any open record */
|
|
457
|
+
void c0_builder_gs(c0_builder *b); /**< GS alone: one step of depth (gs, gs_name is depth 2) */
|
|
458
|
+
void c0_builder_gs_name(c0_builder *b, const uint8_t *name, size_t nlen); /**< GS name; ends any open record */
|
|
459
|
+
void c0_builder_rs(c0_builder *b); /**< RS: open a record */
|
|
460
|
+
void c0_builder_rs_value(c0_builder *b, const uint8_t *value, size_t vlen); /**< RS and the record's first value */
|
|
461
|
+
void c0_builder_us(c0_builder *b); /**< US alone (an empty unit, or before DLE / ENQ) */
|
|
462
|
+
void c0_builder_us_value(c0_builder *b, const uint8_t *value, size_t vlen); /**< US and the value that follows it */
|
|
463
|
+
void c0_builder_sub(c0_builder *b); /**< SUB alone. C0-DIFF only: in a data body SUB is an error */
|
|
464
|
+
void c0_builder_sub_value(c0_builder *b, const uint8_t *value, size_t vlen); /**< SUB and the new side of a substitution (C0-DIFF only): rs old, sub new is a replace-all */
|
|
465
|
+
void c0_builder_soh(c0_builder *b); /**< SOH alone: before RS, a header row */
|
|
466
|
+
void c0_builder_soh_name(c0_builder *b, const uint8_t *name, size_t nlen); /**< SOH key */
|
|
467
|
+
void c0_builder_dle(c0_builder *b); /**< DLE: open a level (saves the record state) */
|
|
468
|
+
void c0_builder_can(c0_builder *b); /**< CAN: close the innermost level; C0_ERR_BAD_STATE if none is open */
|
|
469
|
+
void c0_builder_bel(c0_builder *b); /**< BEL alone: the element that follows is dead */
|
|
470
|
+
void c0_builder_bel_text(c0_builder *b, const uint8_t *text, size_t tlen); /**< BEL and a note; empty text is C0_ERR_BAD_STATE */
|
|
471
|
+
void c0_builder_enq(c0_builder *b, const uint8_t *name, size_t nlen); /**< ENQ name */
|
|
472
|
+
void c0_builder_enq_path(c0_builder *b, const c0_bytes *segments, size_t count); /**< ENQ STX seg US seg [US seg] ETX (one segment: ENQ name) */
|
|
473
|
+
void c0_builder_syn(c0_builder *b); /**< SYN; soh_name + us_value pairs that follow are the heading */
|
|
474
|
+
void c0_builder_eot(c0_builder *b); /**< EOT */
|
|
475
|
+
void c0_builder_etb(c0_builder *b); /**< ETB alone */
|
|
476
|
+
void c0_builder_etb_payload(c0_builder *b, const uint8_t *payload, size_t plen); /**< ETB and a plain payload */
|
|
477
|
+
void c0_builder_em(c0_builder *b, const uint8_t *tail, size_t tlen); /**< EM and the raw tail; nothing may follow */
|
|
478
|
+
|
|
479
|
+
void c0_builder_fs_str(c0_builder *b, const char *name);
|
|
480
|
+
void c0_builder_gs_str(c0_builder *b, const char *name);
|
|
481
|
+
void c0_builder_rs_str(c0_builder *b, const char *value);
|
|
482
|
+
void c0_builder_us_str(c0_builder *b, const char *value);
|
|
483
|
+
void c0_builder_sub_str(c0_builder *b, const char *value);
|
|
484
|
+
void c0_builder_soh_str(c0_builder *b, const char *name);
|
|
485
|
+
void c0_builder_bel_str(c0_builder *b, const char *text);
|
|
486
|
+
void c0_builder_enq_str(c0_builder *b, const char *name);
|
|
487
|
+
void c0_builder_enq_path_str(c0_builder *b, const char *const *segments, size_t count);
|
|
488
|
+
void c0_builder_etb_str(c0_builder *b, const char *payload);
|
|
489
|
+
void c0_builder_em_str(c0_builder *b, const char *tail);
|
|
490
|
+
|
|
491
|
+
/* composed: cannot write ill-formed output */
|
|
492
|
+
|
|
493
|
+
/* documents */
|
|
494
|
+
void c0_build_syn(c0_builder *b); /**< SYN; keyed units until the next element are the heading */
|
|
495
|
+
void c0_build_eot(c0_builder *b); /**< EOT */
|
|
496
|
+
void c0_build_etb(c0_builder *b, const uint8_t *payload, size_t plen); /**< ETB and an optional plain payload (NULL for none) */
|
|
497
|
+
void c0_build_em(c0_builder *b, const uint8_t *tail, size_t tlen); /**< EM and the raw tail; nothing may follow */
|
|
498
|
+
|
|
499
|
+
/* structure */
|
|
500
|
+
void c0_build_file(c0_builder *b, const uint8_t *name, size_t nlen); /**< FS name */
|
|
501
|
+
void c0_build_group(c0_builder *b, const uint8_t *name, size_t nlen); /**< GS name */
|
|
502
|
+
void c0_build_section(c0_builder *b, const uint8_t *name, size_t nlen, size_t depth); /**< GS×depth name */
|
|
503
|
+
void c0_build_header(c0_builder *b, const c0_bytes *names, size_t count); /**< SOH RS name US name … */
|
|
504
|
+
|
|
505
|
+
/* records */
|
|
506
|
+
void c0_build_record(c0_builder *b, const c0_bytes *fields, size_t count); /**< RS and `count` units; count 0 opens an empty record */
|
|
507
|
+
void c0_build_field(c0_builder *b, const uint8_t *value, size_t vlen); /**< next unit of the open record (opens one if none) */
|
|
508
|
+
void c0_build_map(c0_builder *b, const c0_bytes *keys, const c0_bytes *values, size_t count); /**< RS and keyed units in the order given */
|
|
509
|
+
void c0_build_key(c0_builder *b, const uint8_t *key, size_t klen, const uint8_t *value, size_t vlen); /**< SOH key US value, in the open record or the heading */
|
|
510
|
+
void c0_build_level_open(c0_builder *b); /**< DLE: a nested level as the next unit; inside, the builder is at the top of a fresh body */
|
|
511
|
+
void c0_build_key_level_open(c0_builder *b, const uint8_t *key, size_t klen); /**< SOH key US DLE */
|
|
512
|
+
void c0_build_level_close(c0_builder *b); /**< CAN */
|
|
513
|
+
void c0_build_list(c0_builder *b, const c0_bytes *items, size_t count); /**< a flat list as the next unit: DLE RS item US item … CAN, or DLE CAN */
|
|
514
|
+
void c0_build_ref(c0_builder *b, const uint8_t *name, size_t nlen); /**< ENQ name as the next unit */
|
|
515
|
+
void c0_build_ref_path(c0_builder *b, const c0_bytes *segments, size_t count); /**< ENQ, then STX seg US seg [US seg] ETX (one segment: plain) */
|
|
516
|
+
void c0_build_note(c0_builder *b, const uint8_t *text, size_t tlen); /**< BEL and the note (plain, or text when it must be) */
|
|
517
|
+
|
|
518
|
+
/* document mode */
|
|
519
|
+
void c0_build_block(c0_builder *b, const uint8_t *text, size_t tlen); /**< a paragraph: a record of one unit */
|
|
520
|
+
void c0_build_item(c0_builder *b, const uint8_t *text, size_t tlen); /**< a list item: the next unit */
|
|
521
|
+
|
|
522
|
+
/* NUL-terminated convenience wrappers */
|
|
523
|
+
void c0_build_etb_str(c0_builder *b, const char *payload);
|
|
524
|
+
void c0_build_file_str(c0_builder *b, const char *name);
|
|
525
|
+
void c0_build_group_str(c0_builder *b, const char *name);
|
|
526
|
+
void c0_build_section_str(c0_builder *b, const char *name, size_t depth);
|
|
527
|
+
void c0_build_header_str(c0_builder *b, const char *const *names, size_t count);
|
|
528
|
+
void c0_build_record_str(c0_builder *b, const char *const *fields, size_t count);
|
|
529
|
+
void c0_build_field_str(c0_builder *b, const char *value);
|
|
530
|
+
void c0_build_map_str(c0_builder *b, const char *const *keys, const char *const *values, size_t count);
|
|
531
|
+
void c0_build_key_str(c0_builder *b, const char *key, const char *value);
|
|
532
|
+
void c0_build_key_level_open_str(c0_builder *b, const char *key);
|
|
533
|
+
void c0_build_list_str(c0_builder *b, const char *const *items, size_t count);
|
|
534
|
+
void c0_build_ref_str(c0_builder *b, const char *name);
|
|
535
|
+
void c0_build_ref_path_str(c0_builder *b, const char *const *segments, size_t count);
|
|
536
|
+
void c0_build_note_str(c0_builder *b, const char *text);
|
|
537
|
+
void c0_build_block_str(c0_builder *b, const char *text);
|
|
538
|
+
void c0_build_item_str(c0_builder *b, const char *text);
|
|
539
|
+
|
|
540
|
+
/* ---- Stream mode (ETB commits) ----------------------------------------- */
|
|
541
|
+
|
|
542
|
+
/** A scan of an append-only log. An unescaped ETB is always a commit, inside
|
|
543
|
+
* text or a level too; a block so cut is damaged and yields nothing. The
|
|
544
|
+
* tail after the last commit is torn unless it holds only layout, notes or
|
|
545
|
+
* EOT. */
|
|
546
|
+
typedef struct {
|
|
547
|
+
const uint8_t *buf;
|
|
548
|
+
size_t len;
|
|
549
|
+
size_t committed_end; /**< just past the last ETB and its payload */
|
|
550
|
+
int torn;
|
|
551
|
+
size_t block_count;
|
|
552
|
+
size_t damaged_count;
|
|
553
|
+
} c0_stream;
|
|
554
|
+
|
|
555
|
+
c0_stream c0_stream_read(const uint8_t *buf, size_t len);
|
|
556
|
+
c0_bytes c0_stream_committed(const c0_stream *s);
|
|
557
|
+
c0_bytes c0_stream_tail(const c0_stream *s);
|
|
558
|
+
|
|
559
|
+
/** Cursor over the committed blocks (previous commit to ETB, marker and
|
|
560
|
+
* payload excluded); *damaged is set for a block cut inside text or a level. */
|
|
561
|
+
typedef struct {
|
|
562
|
+
const uint8_t *buf;
|
|
563
|
+
size_t stop;
|
|
564
|
+
size_t pos;
|
|
565
|
+
size_t block_start;
|
|
566
|
+
int in_text;
|
|
567
|
+
size_t depth;
|
|
568
|
+
} c0_block_iter;
|
|
569
|
+
|
|
570
|
+
c0_block_iter c0_stream_blocks(const c0_stream *s);
|
|
571
|
+
int c0_next_block(c0_block_iter *it, c0_bytes *out, int *damaged);
|
|
572
|
+
|
|
573
|
+
/** Copy the undamaged committed blocks, without their ETB markers and
|
|
574
|
+
* payloads, into `out` (room for committed_end bytes suffices). Returns the
|
|
575
|
+
* length. Read the result with c0_document_read to get the records. */
|
|
576
|
+
size_t c0_stream_undamaged(const c0_stream *s, uint8_t *out);
|
|
577
|
+
|
|
578
|
+
/** Position just past the last commit, found by a backward scan (a shortcut
|
|
579
|
+
* for well-formed input; c0_stream_read is authoritative). */
|
|
580
|
+
size_t c0_stream_last_commit_end(const uint8_t *buf, size_t len);
|
|
581
|
+
|
|
582
|
+
/* ---- Pretty form (Unicode Control Pictures, U+2400 block) -------------- */
|
|
583
|
+
|
|
584
|
+
typedef enum {
|
|
585
|
+
C0_PRETTY_COMPACT = 0, /**< one element per line, no padding */
|
|
586
|
+
C0_PRETTY_ALIGNED, /**< table columns aligned */
|
|
587
|
+
C0_PRETTY_SPACED /**< aligned, plus a space after each prefix and around ␟ */
|
|
588
|
+
} c0_pretty_mode;
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Format compact bytes as pretty form: a malloc'd, NUL-terminated UTF-8
|
|
592
|
+
* string the caller frees; *outlen (if not NULL) gets its length. `indent`
|
|
593
|
+
* defaults to two spaces when NULL. NULL on error: C0_ERR_OOM, a syntax
|
|
594
|
+
* error, or C0_ERR_SYNTAX with msg "glyph in data" when a value holds a
|
|
595
|
+
* Control Picture character and cannot be shown unambiguously.
|
|
596
|
+
*/
|
|
597
|
+
char *c0_pretty_format(const uint8_t *buf, size_t len, const char *indent, c0_pretty_mode mode, size_t *outlen, c0_error *err);
|
|
598
|
+
|
|
599
|
+
/** Parse pretty form back to compact bytes: glyphs become codes; outside text
|
|
600
|
+
* whitespace touching a glyph or a line edge is dropped. Returns a malloc'd
|
|
601
|
+
* buffer the caller frees (NULL on allocation failure); *outlen gets the length. */
|
|
602
|
+
uint8_t *c0_pretty_parse(const char *str, size_t len, size_t *outlen);
|
|
603
|
+
|
|
604
|
+
#ifdef __cplusplus
|
|
605
|
+
}
|
|
606
|
+
#endif
|
|
607
|
+
|
|
608
|
+
/** ====================================================================== */
|
|
609
|
+
#ifdef C0_IMPLEMENTATION
|
|
610
|
+
|
|
611
|
+
#include <stdlib.h>
|
|
612
|
+
#include <string.h>
|
|
613
|
+
|
|
614
|
+
/* ---- tables ------------------------------------------------------------ */
|
|
615
|
+
|
|
616
|
+
static const unsigned char c0__assigned[32] = {
|
|
617
|
+
0, 1, 1, 1, 1, 1, 0, 1, /* 0x00-0x07: SOH STX ETX EOT ENQ BEL */
|
|
618
|
+
0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0F */
|
|
619
|
+
1, 0, 0, 0, 0, 0, 1, 1, /* 0x10-0x17: DLE SYN ETB */
|
|
620
|
+
1, 1, 1, 0, 1, 1, 1, 1 /* 0x18-0x1F: CAN EM SUB FS GS RS US */
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
static const unsigned char c0__text_escaped[32] = {
|
|
624
|
+
0, 0, 1, 1, 1, 0, 0, 0, /* STX ETX EOT */
|
|
625
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
626
|
+
1, 0, 0, 0, 0, 0, 1, 1, /* DLE SYN ETB */
|
|
627
|
+
0, 1, 0, 0, 0, 0, 0, 0 /* EM */
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
int c0_is_assigned(uint8_t byte) { return byte < 0x20 && c0__assigned[byte]; }
|
|
631
|
+
int c0_is_layout(uint8_t byte) { return byte == C0_SP || byte == C0_HT || byte == C0_LF || byte == C0_CR; }
|
|
632
|
+
int c0_is_text_escaped(uint8_t byte) { return byte < 0x20 && c0__text_escaped[byte]; }
|
|
633
|
+
|
|
634
|
+
int c0_is_plain(const uint8_t *p, size_t len) {
|
|
635
|
+
size_t i;
|
|
636
|
+
if (len == 0) return 1;
|
|
637
|
+
if (c0_is_layout(p[0]) || c0_is_layout(p[len - 1])) return 0;
|
|
638
|
+
for (i = 0; i < len; i++) {
|
|
639
|
+
if (p[i] < 0x20) return 0;
|
|
640
|
+
}
|
|
641
|
+
return 1;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
int c0_is_name(const uint8_t *p, size_t len) { return c0_is_plain(p, len); }
|
|
645
|
+
|
|
646
|
+
c0_bytes c0_bytes_of(const uint8_t *ptr, size_t len) {
|
|
647
|
+
c0_bytes b;
|
|
648
|
+
b.ptr = ptr;
|
|
649
|
+
b.len = len;
|
|
650
|
+
return b;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
c0_bytes c0_trim(c0_bytes raw) {
|
|
654
|
+
size_t s = 0, e = raw.len;
|
|
655
|
+
while (s < e && c0_is_layout(raw.ptr[s])) s++;
|
|
656
|
+
while (e > s && c0_is_layout(raw.ptr[e - 1])) e--;
|
|
657
|
+
raw.ptr += s;
|
|
658
|
+
raw.len = e - s;
|
|
659
|
+
return raw;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
size_t c0_decode_text(const uint8_t *in, size_t len, uint8_t *out) {
|
|
663
|
+
size_t i = 0, n = 0;
|
|
664
|
+
while (i < len) {
|
|
665
|
+
if (in[i] == C0_DLE) {
|
|
666
|
+
i++;
|
|
667
|
+
if (i >= len) break;
|
|
668
|
+
}
|
|
669
|
+
out[n++] = in[i++];
|
|
670
|
+
}
|
|
671
|
+
return n;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/* ---- errors ------------------------------------------------------------ */
|
|
675
|
+
|
|
676
|
+
static void c0__fail(c0_error *err, c0_status code, size_t pos, const char *msg) {
|
|
677
|
+
if (err && err->code == C0_OK) {
|
|
678
|
+
err->code = code;
|
|
679
|
+
err->pos = pos;
|
|
680
|
+
err->msg = msg;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
static void c0__err_clear(c0_error *err) {
|
|
685
|
+
if (err) {
|
|
686
|
+
err->code = C0_OK;
|
|
687
|
+
err->pos = 0;
|
|
688
|
+
err->msg = "";
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
static c0_bytes c0__slice(const uint8_t *buf, size_t s, size_t e) {
|
|
693
|
+
c0_bytes b;
|
|
694
|
+
b.ptr = buf + s;
|
|
695
|
+
b.len = e > s ? e - s : 0;
|
|
696
|
+
return b;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/* ---- scanning primitives (c0.cr `Scan`) --------------------------------
|
|
700
|
+
* Each takes a buffer and a position and returns the position after the
|
|
701
|
+
* construct. On malformed input it records the error and returns `stop`, so
|
|
702
|
+
* callers' loops end; callers that care check err->code. */
|
|
703
|
+
|
|
704
|
+
static size_t c0__layout(const uint8_t *buf, size_t pos, size_t stop) {
|
|
705
|
+
while (pos < stop && c0_is_layout(buf[pos])) pos++;
|
|
706
|
+
return pos;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
static size_t c0__plain(const uint8_t *buf, size_t pos, size_t stop) {
|
|
710
|
+
while (pos < stop && buf[pos] >= 0x20) pos++;
|
|
711
|
+
return pos;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/* At the STX of a text: the position after its ETX. */
|
|
715
|
+
static size_t c0__text(const uint8_t *buf, size_t pos, size_t stop, c0_error *err) {
|
|
716
|
+
size_t start = pos;
|
|
717
|
+
pos++;
|
|
718
|
+
while (pos < stop) {
|
|
719
|
+
uint8_t b = buf[pos];
|
|
720
|
+
if (b == C0_DLE) {
|
|
721
|
+
pos++;
|
|
722
|
+
if (pos >= stop) {
|
|
723
|
+
c0__fail(err, C0_ERR_UNEXPECTED_END, pos, "DLE at end of text");
|
|
724
|
+
return stop;
|
|
725
|
+
}
|
|
726
|
+
if (!c0_is_text_escaped(buf[pos])) {
|
|
727
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "DLE before a byte that is not one of the seven");
|
|
728
|
+
return stop;
|
|
729
|
+
}
|
|
730
|
+
pos++;
|
|
731
|
+
} else if (b == C0_ETX) {
|
|
732
|
+
return pos + 1;
|
|
733
|
+
} else if (b == C0_STX) {
|
|
734
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "STX inside text (text does not nest)");
|
|
735
|
+
return stop;
|
|
736
|
+
} else if (b == C0_ETB || b == C0_EOT || b == C0_SYN || b == C0_EM) {
|
|
737
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "framing inside open text");
|
|
738
|
+
return stop;
|
|
739
|
+
} else {
|
|
740
|
+
pos++;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
c0__fail(err, C0_ERR_UNEXPECTED_END, start, "text is not closed");
|
|
744
|
+
return stop;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/* At the DLE of a level: the position after its matching CAN. */
|
|
748
|
+
static size_t c0__level(const uint8_t *buf, size_t pos, size_t stop, c0_error *err) {
|
|
749
|
+
size_t start = pos, depth = 1;
|
|
750
|
+
pos++;
|
|
751
|
+
while (pos < stop) {
|
|
752
|
+
uint8_t b = buf[pos];
|
|
753
|
+
if (b >= 0x20) {
|
|
754
|
+
pos++;
|
|
755
|
+
} else if (b == C0_STX) {
|
|
756
|
+
pos = c0__text(buf, pos, stop, err);
|
|
757
|
+
if (err->code) return stop;
|
|
758
|
+
} else if (b == C0_DLE) {
|
|
759
|
+
depth++;
|
|
760
|
+
pos++;
|
|
761
|
+
} else if (b == C0_CAN) {
|
|
762
|
+
depth--;
|
|
763
|
+
pos++;
|
|
764
|
+
if (depth == 0) return pos;
|
|
765
|
+
} else if (b == C0_ETB || b == C0_EOT || b == C0_SYN || b == C0_EM) {
|
|
766
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "framing inside open level");
|
|
767
|
+
return stop;
|
|
768
|
+
} else {
|
|
769
|
+
pos++;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
c0__fail(err, C0_ERR_UNEXPECTED_END, start, "level is not closed");
|
|
773
|
+
return stop;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
static size_t c0__comment(const uint8_t *buf, size_t pos, size_t stop, c0_error *err);
|
|
777
|
+
static size_t c0__record_end(const uint8_t *buf, size_t pos, size_t stop, c0_error *err);
|
|
778
|
+
static size_t c0__keyed_unit_end(const uint8_t *buf, size_t pos, size_t stop, c0_error *err);
|
|
779
|
+
|
|
780
|
+
/* Is the BEL at pos followed (after layout) by a code other than STX/DLE,
|
|
781
|
+
* i.e. does it kill a record-level element? */
|
|
782
|
+
static int c0__bel_kills_element(const uint8_t *buf, size_t pos, size_t stop) {
|
|
783
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
784
|
+
return p2 < stop && buf[p2] < 0x20 && buf[p2] != C0_STX && buf[p2] != C0_DLE;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/* From just after a US (or a key's US): the end of one unit's value, i.e.
|
|
788
|
+
* the next control code at this depth that does not belong to the value.
|
|
789
|
+
* Layout at the edges stays inside the returned range. */
|
|
790
|
+
static size_t c0__unit(const uint8_t *buf, size_t pos, size_t stop, c0_error *err) {
|
|
791
|
+
while (pos < stop) {
|
|
792
|
+
uint8_t b = buf[pos];
|
|
793
|
+
if (b >= 0x20 || c0_is_layout(b)) {
|
|
794
|
+
pos++;
|
|
795
|
+
} else if (b == C0_STX) {
|
|
796
|
+
pos = c0__text(buf, pos, stop, err);
|
|
797
|
+
if (err->code) return stop;
|
|
798
|
+
} else if (b == C0_DLE) {
|
|
799
|
+
pos = c0__level(buf, pos, stop, err);
|
|
800
|
+
if (err->code) return stop;
|
|
801
|
+
} else if (b == C0_ENQ) {
|
|
802
|
+
pos++;
|
|
803
|
+
} else if (b == C0_BEL) {
|
|
804
|
+
if (c0__bel_kills_element(buf, pos, stop)) return pos;
|
|
805
|
+
pos = c0__comment(buf, pos, stop, err);
|
|
806
|
+
if (err->code) return stop;
|
|
807
|
+
} else {
|
|
808
|
+
return pos;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
return pos;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/* At a BEL: skip the note (plain or text) or the whole dead element. */
|
|
815
|
+
static size_t c0__comment(const uint8_t *buf, size_t pos, size_t stop, c0_error *err) {
|
|
816
|
+
uint8_t b;
|
|
817
|
+
pos = c0__layout(buf, pos + 1, stop);
|
|
818
|
+
if (pos >= stop) {
|
|
819
|
+
c0__fail(err, C0_ERR_UNEXPECTED_END, pos, "BEL at end of input");
|
|
820
|
+
return stop;
|
|
821
|
+
}
|
|
822
|
+
b = buf[pos];
|
|
823
|
+
if (b >= 0x20) return c0__plain(buf, pos, stop);
|
|
824
|
+
if (b == C0_STX) return c0__text(buf, pos, stop, err);
|
|
825
|
+
if (b == C0_DLE) return c0__level(buf, pos, stop, err);
|
|
826
|
+
if (b == C0_RS) return c0__record_end(buf, pos + 1, stop, err);
|
|
827
|
+
if (b == C0_SOH) {
|
|
828
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
829
|
+
if (p2 < stop && buf[p2] == C0_RS) return c0__record_end(buf, p2 + 1, stop, err);
|
|
830
|
+
return c0__keyed_unit_end(buf, pos + 1, stop, err);
|
|
831
|
+
}
|
|
832
|
+
if (b == C0_GS) {
|
|
833
|
+
while (pos < stop && (buf[pos] == C0_GS || c0_is_layout(buf[pos]))) pos++;
|
|
834
|
+
return c0__plain(buf, pos, stop);
|
|
835
|
+
}
|
|
836
|
+
if (b == C0_FS) return c0__plain(buf, pos + 1, stop);
|
|
837
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "BEL before a code that is not an element");
|
|
838
|
+
return stop;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/* From just after an RS: the end of the record, i.e. the next record-level
|
|
842
|
+
* code at this depth. */
|
|
843
|
+
static size_t c0__record_end(const uint8_t *buf, size_t pos, size_t stop, c0_error *err) {
|
|
844
|
+
while (pos < stop) {
|
|
845
|
+
uint8_t b = buf[pos];
|
|
846
|
+
if (b >= 0x20 || c0_is_layout(b) || b == C0_US || b == C0_ENQ) {
|
|
847
|
+
pos++;
|
|
848
|
+
} else if (b == C0_SOH) {
|
|
849
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
850
|
+
if (p2 < stop && buf[p2] == C0_RS) return pos; /* a header row */
|
|
851
|
+
pos++;
|
|
852
|
+
} else if (b == C0_STX) {
|
|
853
|
+
pos = c0__text(buf, pos, stop, err);
|
|
854
|
+
if (err->code) return stop;
|
|
855
|
+
} else if (b == C0_DLE) {
|
|
856
|
+
pos = c0__level(buf, pos, stop, err);
|
|
857
|
+
if (err->code) return stop;
|
|
858
|
+
} else if (b == C0_BEL) {
|
|
859
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
860
|
+
if (p2 < stop && (buf[p2] == C0_RS || buf[p2] == C0_GS || buf[p2] == C0_FS)) return pos;
|
|
861
|
+
if (p2 < stop && buf[p2] == C0_SOH) {
|
|
862
|
+
size_t p3 = c0__layout(buf, p2 + 1, stop);
|
|
863
|
+
if (p3 < stop && buf[p3] == C0_RS) return pos;
|
|
864
|
+
}
|
|
865
|
+
pos = c0__comment(buf, pos, stop, err);
|
|
866
|
+
if (err->code) return stop;
|
|
867
|
+
} else {
|
|
868
|
+
return pos;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
return pos;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/* From just after an SOH that is not a header row: past the key, US and value. */
|
|
875
|
+
static size_t c0__keyed_unit_end(const uint8_t *buf, size_t pos, size_t stop, c0_error *err) {
|
|
876
|
+
pos = c0__layout(buf, pos, stop);
|
|
877
|
+
pos = c0__plain(buf, pos, stop);
|
|
878
|
+
if (!(pos < stop && buf[pos] == C0_US)) {
|
|
879
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "key without a value (no US)");
|
|
880
|
+
return stop;
|
|
881
|
+
}
|
|
882
|
+
return c0__unit(buf, pos + 1, stop, err);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
static size_t c0__trim_end(const uint8_t *buf, size_t start, size_t stop) {
|
|
886
|
+
while (stop > start && c0_is_layout(buf[stop - 1])) stop--;
|
|
887
|
+
return stop;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
#define C0__SCAN_WRAP(name, inner) \
|
|
891
|
+
size_t name(const uint8_t *buf, size_t pos, size_t stop, c0_error *err) { \
|
|
892
|
+
c0_error local = {C0_OK, 0, ""}; \
|
|
893
|
+
if (!err) err = &local; \
|
|
894
|
+
c0__err_clear(err); \
|
|
895
|
+
return inner(buf, pos, stop, err); \
|
|
896
|
+
}
|
|
897
|
+
size_t c0_scan_layout(const uint8_t *buf, size_t pos, size_t stop) { return c0__layout(buf, pos, stop); }
|
|
898
|
+
size_t c0_scan_plain(const uint8_t *buf, size_t pos, size_t stop) { return c0__plain(buf, pos, stop); }
|
|
899
|
+
C0__SCAN_WRAP(c0_scan_text, c0__text)
|
|
900
|
+
C0__SCAN_WRAP(c0_scan_level, c0__level)
|
|
901
|
+
C0__SCAN_WRAP(c0_scan_unit, c0__unit)
|
|
902
|
+
C0__SCAN_WRAP(c0_scan_comment, c0__comment)
|
|
903
|
+
C0__SCAN_WRAP(c0_scan_record_end, c0__record_end)
|
|
904
|
+
|
|
905
|
+
/* ---- values ------------------------------------------------------------ */
|
|
906
|
+
|
|
907
|
+
c0_unit_kind c0_unit_type(c0_bytes raw) {
|
|
908
|
+
c0_bytes t = c0_trim(raw);
|
|
909
|
+
if (t.len == 0) return C0_UNIT_PLAIN;
|
|
910
|
+
switch (t.ptr[0]) {
|
|
911
|
+
case C0_STX: return C0_UNIT_TEXT;
|
|
912
|
+
case C0_DLE: return C0_UNIT_LEVEL;
|
|
913
|
+
case C0_ENQ: return C0_UNIT_REF;
|
|
914
|
+
default: return C0_UNIT_PLAIN;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/* From pos to the end of t only notes (and layout) may follow. */
|
|
919
|
+
static c0_status c0__only_notes_after(c0_bytes t, size_t pos) {
|
|
920
|
+
c0_error err = {C0_OK, 0, ""};
|
|
921
|
+
pos = c0__layout(t.ptr, pos, t.len);
|
|
922
|
+
while (pos < t.len) {
|
|
923
|
+
if (t.ptr[pos] != C0_BEL) return C0_ERR_SYNTAX;
|
|
924
|
+
pos = c0__layout(t.ptr, c0__comment(t.ptr, pos, t.len, &err), t.len);
|
|
925
|
+
if (err.code) return err.code;
|
|
926
|
+
}
|
|
927
|
+
return C0_OK;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
c0_status c0_value(c0_bytes raw, uint8_t *scratch, c0_bytes *out) {
|
|
931
|
+
c0_bytes t = c0_trim(raw);
|
|
932
|
+
c0_error err = {C0_OK, 0, ""};
|
|
933
|
+
size_t i;
|
|
934
|
+
out->ptr = t.ptr;
|
|
935
|
+
out->len = 0;
|
|
936
|
+
if (t.len == 0) return C0_OK;
|
|
937
|
+
switch (t.ptr[0]) {
|
|
938
|
+
case C0_STX: {
|
|
939
|
+
size_t stop = c0__text(t.ptr, 0, t.len, &err);
|
|
940
|
+
c0_status st;
|
|
941
|
+
if (err.code) return err.code;
|
|
942
|
+
st = c0__only_notes_after(t, stop);
|
|
943
|
+
if (st) return st;
|
|
944
|
+
out->ptr = t.ptr + 1;
|
|
945
|
+
out->len = stop - 2;
|
|
946
|
+
for (i = 0; i < out->len; i++) {
|
|
947
|
+
if (out->ptr[i] == C0_DLE) {
|
|
948
|
+
if (!scratch) return C0_ERR_NEEDS_DECODE;
|
|
949
|
+
out->len = c0_decode_text(out->ptr, out->len, scratch);
|
|
950
|
+
out->ptr = scratch;
|
|
951
|
+
break;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return C0_OK;
|
|
955
|
+
}
|
|
956
|
+
case C0_BEL:
|
|
957
|
+
return c0__only_notes_after(t, 0); /* a note or a dead level: the unit is empty */
|
|
958
|
+
case C0_DLE:
|
|
959
|
+
case C0_ENQ:
|
|
960
|
+
return C0_ERR_NOT_SCALAR;
|
|
961
|
+
default:
|
|
962
|
+
for (i = 0; i < t.len; i++) {
|
|
963
|
+
uint8_t b = t.ptr[i];
|
|
964
|
+
if (b == C0_BEL) {
|
|
965
|
+
c0_status st = c0__only_notes_after(t, i);
|
|
966
|
+
if (st) return st;
|
|
967
|
+
*out = c0_trim(c0__slice(t.ptr, 0, i));
|
|
968
|
+
return C0_OK;
|
|
969
|
+
}
|
|
970
|
+
if (b < 0x20) return C0_ERR_SYNTAX; /* layout or control code inside a plain value */
|
|
971
|
+
}
|
|
972
|
+
*out = t;
|
|
973
|
+
return C0_OK;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
c0_body c0_body_view(const uint8_t *buf, size_t len) {
|
|
978
|
+
c0_body b;
|
|
979
|
+
b.buf = buf;
|
|
980
|
+
b.start = 0;
|
|
981
|
+
b.stop = len;
|
|
982
|
+
return b;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
c0_status c0_unit_level(c0_bytes raw, c0_body *out) {
|
|
986
|
+
c0_bytes t = c0_trim(raw);
|
|
987
|
+
c0_error err = {C0_OK, 0, ""};
|
|
988
|
+
size_t stop;
|
|
989
|
+
if (!(t.len > 0 && t.ptr[0] == C0_DLE)) return C0_ERR_NOT_LEVEL;
|
|
990
|
+
stop = c0__level(t.ptr, 0, t.len, &err);
|
|
991
|
+
if (err.code) return err.code;
|
|
992
|
+
out->buf = t.ptr;
|
|
993
|
+
out->start = 1;
|
|
994
|
+
out->stop = stop - 1;
|
|
995
|
+
return C0_OK;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
c0_status c0_unit_ref(c0_bytes raw, c0_ref *out) {
|
|
999
|
+
c0_bytes t = c0_trim(raw);
|
|
1000
|
+
size_t p;
|
|
1001
|
+
out->count = 0;
|
|
1002
|
+
if (!(t.len > 0 && t.ptr[0] == C0_ENQ)) return C0_ERR_NOT_REFERENCE;
|
|
1003
|
+
p = c0__layout(t.ptr, 1, t.len);
|
|
1004
|
+
if (p < t.len && t.ptr[p] == C0_STX) {
|
|
1005
|
+
c0_error err = {C0_OK, 0, ""};
|
|
1006
|
+
size_t stop = c0__text(t.ptr, p, t.len, &err), s, i;
|
|
1007
|
+
if (err.code) return err.code;
|
|
1008
|
+
s = p + 1;
|
|
1009
|
+
for (i = s; i <= stop - 1; i++) {
|
|
1010
|
+
if (i == stop - 1 || t.ptr[i] == C0_US) {
|
|
1011
|
+
if (out->count == 3) return C0_ERR_SYNTAX; /* more than three segments */
|
|
1012
|
+
if (i == s) return C0_ERR_SYNTAX; /* empty segment */
|
|
1013
|
+
out->seg[out->count] = c0__slice(t.ptr, s, i);
|
|
1014
|
+
if (!c0_is_name(out->seg[out->count].ptr, out->seg[out->count].len)) return C0_ERR_SYNTAX;
|
|
1015
|
+
out->count++;
|
|
1016
|
+
s = i + 1;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return C0_OK;
|
|
1020
|
+
} else {
|
|
1021
|
+
c0_bytes name = c0_trim(c0__slice(t.ptr, p, t.len));
|
|
1022
|
+
if (name.len == 0) return C0_ERR_SYNTAX; /* reference without a name */
|
|
1023
|
+
if (!c0_is_name(name.ptr, name.len)) return C0_ERR_SYNTAX;
|
|
1024
|
+
out->seg[0] = name;
|
|
1025
|
+
out->count = 1;
|
|
1026
|
+
return C0_OK;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
/* ---- records ----------------------------------------------------------- */
|
|
1031
|
+
|
|
1032
|
+
int c0_record_keyed(c0_record r) {
|
|
1033
|
+
size_t p = c0__layout(r.buf, r.start, r.stop);
|
|
1034
|
+
c0_error err = {C0_OK, 0, ""};
|
|
1035
|
+
while (p < r.stop && r.buf[p] == C0_BEL) {
|
|
1036
|
+
size_t p2 = c0__layout(r.buf, p + 1, r.stop);
|
|
1037
|
+
if (p2 < r.stop && r.buf[p2] == C0_SOH) return 1;
|
|
1038
|
+
if (p2 < r.stop && r.buf[p2] < 0x20 && r.buf[p2] != C0_STX && r.buf[p2] != C0_DLE) break;
|
|
1039
|
+
p = c0__layout(r.buf, c0__comment(r.buf, p, r.stop, &err), r.stop);
|
|
1040
|
+
if (err.code) return 0;
|
|
1041
|
+
}
|
|
1042
|
+
return p < r.stop && r.buf[p] == C0_SOH;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
c0_unit_iter c0_record_units(c0_record r) {
|
|
1046
|
+
c0_unit_iter it;
|
|
1047
|
+
it.buf = r.buf;
|
|
1048
|
+
it.pos = r.start;
|
|
1049
|
+
it.stop = r.stop;
|
|
1050
|
+
it.done = c0_record_keyed(r);
|
|
1051
|
+
c0__err_clear(&it.err);
|
|
1052
|
+
return it;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
int c0_next_unit(c0_unit_iter *it, c0_bytes *raw) {
|
|
1056
|
+
size_t s, e;
|
|
1057
|
+
if (it->done) return 0;
|
|
1058
|
+
s = it->pos;
|
|
1059
|
+
e = c0__unit(it->buf, s, it->stop, &it->err);
|
|
1060
|
+
if (it->err.code) {
|
|
1061
|
+
it->done = 1;
|
|
1062
|
+
return 0;
|
|
1063
|
+
}
|
|
1064
|
+
*raw = c0__slice(it->buf, s, e);
|
|
1065
|
+
if (e < it->stop && it->buf[e] == C0_US) {
|
|
1066
|
+
it->pos = e + 1;
|
|
1067
|
+
} else {
|
|
1068
|
+
it->done = 1;
|
|
1069
|
+
}
|
|
1070
|
+
return 1;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
size_t c0_record_count(c0_record r) {
|
|
1074
|
+
c0_unit_iter it = c0_record_units(r);
|
|
1075
|
+
c0_bytes u;
|
|
1076
|
+
size_t n = 0;
|
|
1077
|
+
while (c0_next_unit(&it, &u)) n++;
|
|
1078
|
+
return n;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
c0_status c0_record_field(c0_record r, size_t n, c0_bytes *raw) {
|
|
1082
|
+
c0_unit_iter it = c0_record_units(r);
|
|
1083
|
+
size_t i = 0;
|
|
1084
|
+
while (c0_next_unit(&it, raw)) {
|
|
1085
|
+
if (i == n) return C0_OK;
|
|
1086
|
+
i++;
|
|
1087
|
+
}
|
|
1088
|
+
return it.err.code ? it.err.code : C0_ERR_NOT_FOUND;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
static c0_pair_iter c0__pairs(const uint8_t *buf, size_t start, size_t stop) {
|
|
1092
|
+
c0_pair_iter it;
|
|
1093
|
+
it.buf = buf;
|
|
1094
|
+
it.pos = start;
|
|
1095
|
+
it.stop = stop;
|
|
1096
|
+
c0__err_clear(&it.err);
|
|
1097
|
+
return it;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
c0_pair_iter c0_record_pairs(c0_record r) { return c0__pairs(r.buf, r.start, r.stop); }
|
|
1101
|
+
|
|
1102
|
+
int c0_next_pair(c0_pair_iter *it, c0_bytes *key, c0_bytes *raw) {
|
|
1103
|
+
const uint8_t *buf = it->buf;
|
|
1104
|
+
while (it->pos < it->stop) {
|
|
1105
|
+
uint8_t b = buf[it->pos];
|
|
1106
|
+
if (b == C0_SOH) {
|
|
1107
|
+
size_t ks = c0__layout(buf, it->pos + 1, it->stop);
|
|
1108
|
+
size_t ke = c0__plain(buf, ks, it->stop), vs, ve;
|
|
1109
|
+
if (!(ke < it->stop && buf[ke] == C0_US)) {
|
|
1110
|
+
c0__fail(&it->err, C0_ERR_SYNTAX, ke, "key without a value (no US)");
|
|
1111
|
+
it->pos = it->stop;
|
|
1112
|
+
return 0;
|
|
1113
|
+
}
|
|
1114
|
+
vs = ke + 1;
|
|
1115
|
+
ve = c0__unit(buf, vs, it->stop, &it->err);
|
|
1116
|
+
if (it->err.code) {
|
|
1117
|
+
it->pos = it->stop;
|
|
1118
|
+
return 0;
|
|
1119
|
+
}
|
|
1120
|
+
*key = c0_trim(c0__slice(buf, ks, ke));
|
|
1121
|
+
*raw = c0__slice(buf, vs, ve);
|
|
1122
|
+
it->pos = ve;
|
|
1123
|
+
return 1;
|
|
1124
|
+
} else if (b == C0_BEL) {
|
|
1125
|
+
it->pos = c0__comment(buf, it->pos, it->stop, &it->err);
|
|
1126
|
+
if (it->err.code) return 0;
|
|
1127
|
+
} else if (c0_is_layout(b)) {
|
|
1128
|
+
it->pos++;
|
|
1129
|
+
} else {
|
|
1130
|
+
it->pos = it->stop;
|
|
1131
|
+
return 0;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
return 0;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
static c0_status c0__pairs_get(c0_pair_iter it, const uint8_t *key, size_t klen, c0_bytes *raw) {
|
|
1138
|
+
c0_bytes k, v;
|
|
1139
|
+
int found = 0;
|
|
1140
|
+
while (c0_next_pair(&it, &k, &v)) {
|
|
1141
|
+
if (k.len == klen && (klen == 0 || memcmp(k.ptr, key, klen) == 0)) {
|
|
1142
|
+
*raw = v;
|
|
1143
|
+
found = 1;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
if (it.err.code) return it.err.code;
|
|
1147
|
+
return found ? C0_OK : C0_ERR_NOT_FOUND;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
c0_status c0_record_get(c0_record r, const uint8_t *key, size_t klen, c0_bytes *raw) {
|
|
1151
|
+
return c0__pairs_get(c0_record_pairs(r), key, klen, raw);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
c0_status c0_record_get_str(c0_record r, const char *key, c0_bytes *raw) {
|
|
1155
|
+
return c0_record_get(r, (const uint8_t *)key, strlen(key), raw);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/* ---- element scanner over a body range ---------------------------------
|
|
1159
|
+
* Yields the next record-level element at this depth: a record, a header
|
|
1160
|
+
* row, a group line or a file line, live or dead. Notes and commits are
|
|
1161
|
+
* skipped. */
|
|
1162
|
+
|
|
1163
|
+
typedef enum { C0__EL_END = 0, C0__EL_RECORD, C0__EL_HEADER, C0__EL_GROUP, C0__EL_FILE } c0__el_kind;
|
|
1164
|
+
|
|
1165
|
+
typedef struct {
|
|
1166
|
+
c0__el_kind kind;
|
|
1167
|
+
int dead;
|
|
1168
|
+
size_t depth; /* GROUP: number of GS */
|
|
1169
|
+
size_t a, b; /* RECORD/HEADER: the record's range; GROUP/FILE: the name's range */
|
|
1170
|
+
size_t at; /* where the element (or its BEL) begins */
|
|
1171
|
+
size_t next; /* position after the element */
|
|
1172
|
+
} c0__el;
|
|
1173
|
+
|
|
1174
|
+
static void c0__el_record(const uint8_t *buf, size_t rs, size_t stop, c0__el *el, c0__el_kind kind, c0_error *err) {
|
|
1175
|
+
size_t e = c0__record_end(buf, rs, stop, err);
|
|
1176
|
+
el->kind = kind;
|
|
1177
|
+
el->a = rs;
|
|
1178
|
+
el->b = c0__trim_end(buf, rs, e);
|
|
1179
|
+
el->next = e;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
static void c0__next_el(const uint8_t *buf, size_t pos, size_t stop, c0__el *el, c0_error *err) {
|
|
1183
|
+
el->dead = 0;
|
|
1184
|
+
el->depth = 0;
|
|
1185
|
+
for (;;) {
|
|
1186
|
+
uint8_t b;
|
|
1187
|
+
pos = c0__layout(buf, pos, stop);
|
|
1188
|
+
el->at = pos;
|
|
1189
|
+
el->kind = C0__EL_END;
|
|
1190
|
+
el->next = pos;
|
|
1191
|
+
if (err->code || pos >= stop) return;
|
|
1192
|
+
b = buf[pos];
|
|
1193
|
+
if (b == C0_BEL) {
|
|
1194
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
1195
|
+
if (p2 >= stop) {
|
|
1196
|
+
c0__fail(err, C0_ERR_UNEXPECTED_END, p2, "BEL at end of input");
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
if (buf[p2] >= 0x20 || buf[p2] == C0_STX) {
|
|
1200
|
+
pos = c0__comment(buf, pos, stop, err); /* a note */
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
el->dead = 1;
|
|
1204
|
+
pos = p2;
|
|
1205
|
+
b = buf[pos];
|
|
1206
|
+
if (b == C0_DLE) {
|
|
1207
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "a nested level is not an item");
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
if (b == C0_RS) {
|
|
1212
|
+
c0__el_record(buf, pos + 1, stop, el, C0__EL_RECORD, err);
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
if (b == C0_SOH) {
|
|
1216
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
1217
|
+
if (p2 < stop && buf[p2] == C0_RS) {
|
|
1218
|
+
c0__el_record(buf, p2 + 1, stop, el, C0__EL_HEADER, err);
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "keyed unit outside a record");
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
if (b == C0_GS) {
|
|
1225
|
+
while (pos < stop && (buf[pos] == C0_GS || c0_is_layout(buf[pos]))) {
|
|
1226
|
+
if (buf[pos] == C0_GS) el->depth++;
|
|
1227
|
+
pos++;
|
|
1228
|
+
}
|
|
1229
|
+
el->kind = C0__EL_GROUP;
|
|
1230
|
+
el->a = pos;
|
|
1231
|
+
el->b = el->next = c0__plain(buf, pos, stop);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
if (b == C0_FS) {
|
|
1235
|
+
el->kind = C0__EL_FILE;
|
|
1236
|
+
el->a = c0__layout(buf, pos + 1, stop);
|
|
1237
|
+
el->b = el->next = c0__plain(buf, el->a, stop);
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
if (el->dead) {
|
|
1241
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "BEL before a code that is not an element");
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
if (b == C0_ETB) {
|
|
1245
|
+
pos = c0__plain(buf, c0__layout(buf, pos + 1, stop), stop);
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
if (b == C0_EOT || b == C0_SYN || b == C0_EM) return; /* the body ends here */
|
|
1249
|
+
if (b == C0_CAN) {
|
|
1250
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "CAN with no open level");
|
|
1251
|
+
} else if (b < 0x20 && !c0__assigned[b]) {
|
|
1252
|
+
c0__fail(err, C0_ERR_UNASSIGNED, pos, "unassigned control code");
|
|
1253
|
+
} else {
|
|
1254
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "unexpected byte at element level");
|
|
1255
|
+
}
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/* ---- bodies: files ------------------------------------------------------ */
|
|
1261
|
+
|
|
1262
|
+
c0_bytes c0_file_name(c0_file f) { return c0_trim(c0__slice(f.buf, f.name_start, f.name_end)); }
|
|
1263
|
+
c0_bytes c0_group_name(c0_group g) { return c0_trim(c0__slice(g.buf, g.name_start, g.name_end)); }
|
|
1264
|
+
|
|
1265
|
+
c0_file_iter c0_body_files(c0_body b) {
|
|
1266
|
+
c0_file_iter it;
|
|
1267
|
+
it.buf = b.buf;
|
|
1268
|
+
it.pos = b.start;
|
|
1269
|
+
it.stop = b.stop;
|
|
1270
|
+
it.first = 1;
|
|
1271
|
+
it.done = 0;
|
|
1272
|
+
c0__err_clear(&it.err);
|
|
1273
|
+
return it;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/* Scan from pos to the next live FS (or the end); *has says whether a live
|
|
1277
|
+
* record, header row or group line was passed. */
|
|
1278
|
+
static void c0__part_end(const uint8_t *buf, size_t pos, size_t stop, c0__el *el, int *has, c0_error *err) {
|
|
1279
|
+
*has = 0;
|
|
1280
|
+
for (;;) {
|
|
1281
|
+
c0__next_el(buf, pos, stop, el, err);
|
|
1282
|
+
if (el->kind == C0__EL_END) return;
|
|
1283
|
+
if (el->kind == C0__EL_FILE && !el->dead) return;
|
|
1284
|
+
if (!el->dead) *has = 1;
|
|
1285
|
+
pos = el->next;
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
int c0_next_file(c0_file_iter *it, c0_file *out) {
|
|
1290
|
+
c0__el el;
|
|
1291
|
+
if (it->done) return 0;
|
|
1292
|
+
out->buf = it->buf;
|
|
1293
|
+
if (it->first) {
|
|
1294
|
+
int has;
|
|
1295
|
+
it->first = 0;
|
|
1296
|
+
c0__part_end(it->buf, it->pos, it->stop, &el, &has, &it->err);
|
|
1297
|
+
if (has || el.kind == C0__EL_END) {
|
|
1298
|
+
out->name_start = out->name_end = it->pos;
|
|
1299
|
+
out->start = it->pos;
|
|
1300
|
+
out->stop = el.at;
|
|
1301
|
+
it->pos = el.at;
|
|
1302
|
+
if (el.kind == C0__EL_END) it->done = 1;
|
|
1303
|
+
return 1;
|
|
1304
|
+
}
|
|
1305
|
+
it->pos = el.at;
|
|
1306
|
+
}
|
|
1307
|
+
c0__next_el(it->buf, it->pos, it->stop, &el, &it->err);
|
|
1308
|
+
if (el.kind != C0__EL_FILE) {
|
|
1309
|
+
it->done = 1;
|
|
1310
|
+
return 0;
|
|
1311
|
+
}
|
|
1312
|
+
out->name_start = el.a;
|
|
1313
|
+
out->name_end = el.b;
|
|
1314
|
+
out->start = el.next;
|
|
1315
|
+
{
|
|
1316
|
+
int has;
|
|
1317
|
+
c0__part_end(it->buf, el.next, it->stop, &el, &has, &it->err);
|
|
1318
|
+
out->stop = el.at;
|
|
1319
|
+
it->pos = el.at;
|
|
1320
|
+
if (el.kind == C0__EL_END) it->done = 1;
|
|
1321
|
+
}
|
|
1322
|
+
return 1;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/* ---- bodies: groups ---------------------------------------------------- */
|
|
1326
|
+
|
|
1327
|
+
static c0_group_iter c0__groups(const uint8_t *buf, size_t start, size_t stop) {
|
|
1328
|
+
c0_group_iter it;
|
|
1329
|
+
it.buf = buf;
|
|
1330
|
+
it.pos = start;
|
|
1331
|
+
it.stop = stop;
|
|
1332
|
+
it.first = 1;
|
|
1333
|
+
it.done = 0;
|
|
1334
|
+
c0__err_clear(&it.err);
|
|
1335
|
+
return it;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
c0_group_iter c0_file_groups(c0_file f) { return c0__groups(f.buf, f.start, f.stop); }
|
|
1339
|
+
c0_group_iter c0_body_groups(c0_body b) { return c0__groups(b.buf, b.start, b.stop); }
|
|
1340
|
+
|
|
1341
|
+
/* Scan group contents from pos: stop at the next live GS or FS line or the
|
|
1342
|
+
* end; *has says whether a live header row or record was passed. */
|
|
1343
|
+
static void c0__group_end(const uint8_t *buf, size_t pos, size_t stop, c0__el *el, int *has, c0_error *err) {
|
|
1344
|
+
*has = 0;
|
|
1345
|
+
for (;;) {
|
|
1346
|
+
c0__next_el(buf, pos, stop, el, err);
|
|
1347
|
+
if (el->kind == C0__EL_END) return;
|
|
1348
|
+
if ((el->kind == C0__EL_GROUP || el->kind == C0__EL_FILE) && !el->dead) return;
|
|
1349
|
+
if (!el->dead && (el->kind == C0__EL_RECORD || el->kind == C0__EL_HEADER)) *has = 1;
|
|
1350
|
+
pos = el->next;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
int c0_next_group(c0_group_iter *it, c0_group *out) {
|
|
1355
|
+
c0__el el;
|
|
1356
|
+
int has;
|
|
1357
|
+
out->buf = it->buf;
|
|
1358
|
+
for (;;) {
|
|
1359
|
+
if (it->done) return 0;
|
|
1360
|
+
if (it->first) {
|
|
1361
|
+
it->first = 0;
|
|
1362
|
+
c0__group_end(it->buf, it->pos, it->stop, &el, &has, &it->err);
|
|
1363
|
+
if (has) {
|
|
1364
|
+
out->depth = 0;
|
|
1365
|
+
out->name_start = out->name_end = it->pos;
|
|
1366
|
+
out->start = it->pos;
|
|
1367
|
+
out->stop = el.at;
|
|
1368
|
+
it->pos = el.at;
|
|
1369
|
+
if (el.kind == C0__EL_END) it->done = 1;
|
|
1370
|
+
return 1;
|
|
1371
|
+
}
|
|
1372
|
+
it->pos = el.at;
|
|
1373
|
+
}
|
|
1374
|
+
c0__next_el(it->buf, it->pos, it->stop, &el, &it->err);
|
|
1375
|
+
if (el.kind == C0__EL_END) {
|
|
1376
|
+
it->done = 1;
|
|
1377
|
+
return 0;
|
|
1378
|
+
}
|
|
1379
|
+
if (el.kind == C0__EL_FILE) { /* body-wide iteration: a new part begins */
|
|
1380
|
+
it->first = 1;
|
|
1381
|
+
it->pos = el.next;
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
/* a live group line (dead ones are passed by c0__group_end) */
|
|
1385
|
+
out->depth = el.depth;
|
|
1386
|
+
out->name_start = el.a;
|
|
1387
|
+
out->name_end = el.b;
|
|
1388
|
+
out->start = el.next;
|
|
1389
|
+
c0__group_end(it->buf, el.next, it->stop, &el, &has, &it->err);
|
|
1390
|
+
out->stop = el.at;
|
|
1391
|
+
it->pos = el.at;
|
|
1392
|
+
if (el.kind == C0__EL_END) it->done = 1;
|
|
1393
|
+
return 1;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
c0_status c0_body_find_group(c0_body b, const uint8_t *name, size_t nlen, c0_group *out) {
|
|
1398
|
+
c0_group_iter it = c0_body_groups(b);
|
|
1399
|
+
while (c0_next_group(&it, out)) {
|
|
1400
|
+
c0_bytes n = c0_group_name(*out);
|
|
1401
|
+
if (n.len == nlen && (nlen == 0 || memcmp(n.ptr, name, nlen) == 0)) return C0_OK;
|
|
1402
|
+
}
|
|
1403
|
+
return it.err.code ? it.err.code : C0_ERR_NOT_FOUND;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
c0_status c0_body_find_group_str(c0_body b, const char *name, c0_group *out) {
|
|
1407
|
+
return c0_body_find_group(b, (const uint8_t *)name, strlen(name), out);
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
int c0_body_bare(c0_body b) {
|
|
1411
|
+
c0_file_iter fi = c0_body_files(b);
|
|
1412
|
+
c0_file f;
|
|
1413
|
+
c0_group_iter gi;
|
|
1414
|
+
c0_group g;
|
|
1415
|
+
size_t n = 0;
|
|
1416
|
+
if (!c0_next_file(&fi, &f)) return 0;
|
|
1417
|
+
if (c0_file_name(f).len != 0) return 0;
|
|
1418
|
+
if (c0_next_file(&fi, &f)) return 0;
|
|
1419
|
+
gi = c0_file_groups(f);
|
|
1420
|
+
while (c0_next_group(&gi, &g)) {
|
|
1421
|
+
n++;
|
|
1422
|
+
if (n > 1 || g.depth != 0) return 0;
|
|
1423
|
+
}
|
|
1424
|
+
return 1;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
int c0_group_header(c0_group g, c0_record *out) {
|
|
1428
|
+
c0_error err = {C0_OK, 0, ""};
|
|
1429
|
+
size_t pos = g.start;
|
|
1430
|
+
c0__el el;
|
|
1431
|
+
for (;;) {
|
|
1432
|
+
c0__next_el(g.buf, pos, g.stop, &el, &err);
|
|
1433
|
+
if (el.kind == C0__EL_END || err.code) return 0;
|
|
1434
|
+
if (el.kind == C0__EL_HEADER && !el.dead) {
|
|
1435
|
+
out->buf = g.buf;
|
|
1436
|
+
out->start = el.a;
|
|
1437
|
+
out->stop = el.b;
|
|
1438
|
+
return 1;
|
|
1439
|
+
}
|
|
1440
|
+
pos = el.next;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
c0_record_iter c0_group_records(c0_group g) {
|
|
1445
|
+
c0_record_iter it;
|
|
1446
|
+
it.buf = g.buf;
|
|
1447
|
+
it.pos = g.start;
|
|
1448
|
+
it.stop = g.stop;
|
|
1449
|
+
it.done = 0;
|
|
1450
|
+
c0__err_clear(&it.err);
|
|
1451
|
+
return it;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
c0_record_iter c0_body_records(c0_body b) {
|
|
1455
|
+
c0_file_iter fi = c0_body_files(b);
|
|
1456
|
+
c0_file f;
|
|
1457
|
+
c0_group_iter gi;
|
|
1458
|
+
c0_group g;
|
|
1459
|
+
c0_record_iter it;
|
|
1460
|
+
if (c0_next_file(&fi, &f)) {
|
|
1461
|
+
gi = c0_file_groups(f);
|
|
1462
|
+
if (c0_next_group(&gi, &g)) return c0_group_records(g);
|
|
1463
|
+
}
|
|
1464
|
+
it.buf = b.buf;
|
|
1465
|
+
it.pos = it.stop = b.start;
|
|
1466
|
+
it.done = 1;
|
|
1467
|
+
c0__err_clear(&it.err);
|
|
1468
|
+
return it;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
int c0_next_record(c0_record_iter *it, c0_record *out) {
|
|
1472
|
+
c0__el el;
|
|
1473
|
+
if (it->done) return 0;
|
|
1474
|
+
for (;;) {
|
|
1475
|
+
c0__next_el(it->buf, it->pos, it->stop, &el, &it->err);
|
|
1476
|
+
if (el.kind == C0__EL_END) {
|
|
1477
|
+
it->done = 1;
|
|
1478
|
+
return 0;
|
|
1479
|
+
}
|
|
1480
|
+
it->pos = el.next;
|
|
1481
|
+
if (el.kind == C0__EL_RECORD && !el.dead) {
|
|
1482
|
+
out->buf = it->buf;
|
|
1483
|
+
out->start = el.a;
|
|
1484
|
+
out->stop = el.b;
|
|
1485
|
+
return 1;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
c0_status c0_group_column(c0_group g, const uint8_t *name, size_t nlen, size_t *index) {
|
|
1491
|
+
c0_record h;
|
|
1492
|
+
c0_unit_iter it;
|
|
1493
|
+
c0_bytes raw, v;
|
|
1494
|
+
size_t i = 0;
|
|
1495
|
+
if (!c0_group_header(g, &h)) return C0_ERR_NOT_FOUND;
|
|
1496
|
+
it = c0_record_units(h);
|
|
1497
|
+
while (c0_next_unit(&it, &raw)) {
|
|
1498
|
+
if (c0_value(raw, NULL, &v) == C0_OK && v.len == nlen && (nlen == 0 || memcmp(v.ptr, name, nlen) == 0)) {
|
|
1499
|
+
*index = i;
|
|
1500
|
+
return C0_OK;
|
|
1501
|
+
}
|
|
1502
|
+
i++;
|
|
1503
|
+
}
|
|
1504
|
+
return C0_ERR_NOT_FOUND;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
c0_status c0_unit_list(c0_bytes raw, c0_unit_iter *items) {
|
|
1508
|
+
c0_body body;
|
|
1509
|
+
c0_status st = c0_unit_level(raw, &body);
|
|
1510
|
+
c0_file_iter fi;
|
|
1511
|
+
c0_file f;
|
|
1512
|
+
c0_group_iter gi;
|
|
1513
|
+
c0_group g;
|
|
1514
|
+
c0_record_iter ri;
|
|
1515
|
+
c0_record r;
|
|
1516
|
+
if (st) return st;
|
|
1517
|
+
items->buf = body.buf;
|
|
1518
|
+
items->pos = items->stop = body.start;
|
|
1519
|
+
items->done = 1;
|
|
1520
|
+
c0__err_clear(&items->err);
|
|
1521
|
+
fi = c0_body_files(body);
|
|
1522
|
+
if (!c0_next_file(&fi, &f)) return C0_OK; /* an empty level: no items */
|
|
1523
|
+
if (c0_file_name(f).len != 0 || c0_next_file(&fi, &f)) return C0_ERR_NOT_LIST;
|
|
1524
|
+
gi = c0_file_groups(f);
|
|
1525
|
+
if (!c0_next_group(&gi, &g)) return C0_OK;
|
|
1526
|
+
if (g.depth != 0 || c0_next_group(&gi, &g)) return C0_ERR_NOT_LIST;
|
|
1527
|
+
if (c0_group_header(g, &r)) return C0_ERR_NOT_LIST;
|
|
1528
|
+
ri = c0_group_records(g);
|
|
1529
|
+
if (!c0_next_record(&ri, &r)) return ri.err.code ? ri.err.code : C0_OK;
|
|
1530
|
+
if (c0_record_keyed(r)) return C0_ERR_NOT_LIST;
|
|
1531
|
+
*items = c0_record_units(r);
|
|
1532
|
+
if (c0_next_record(&ri, &r)) return C0_ERR_NOT_LIST;
|
|
1533
|
+
return C0_OK;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
/* ---- documents --------------------------------------------------------- */
|
|
1537
|
+
|
|
1538
|
+
/* Scan from the start of a body to its end: EOT, SYN, EM or the end of
|
|
1539
|
+
* input at depth zero, skipping text and levels. */
|
|
1540
|
+
static size_t c0__body_end(const uint8_t *buf, size_t pos, size_t len, c0_error *err) {
|
|
1541
|
+
while (pos < len) {
|
|
1542
|
+
uint8_t b = buf[pos];
|
|
1543
|
+
if (b >= 0x20) {
|
|
1544
|
+
pos++;
|
|
1545
|
+
} else if (b == C0_STX) {
|
|
1546
|
+
pos = c0__text(buf, pos, len, err);
|
|
1547
|
+
if (err->code) return len;
|
|
1548
|
+
} else if (b == C0_DLE) {
|
|
1549
|
+
pos = c0__level(buf, pos, len, err);
|
|
1550
|
+
if (err->code) return len;
|
|
1551
|
+
} else if (b == C0_EOT || b == C0_SYN || b == C0_EM) {
|
|
1552
|
+
return pos;
|
|
1553
|
+
} else if (b == C0_CAN) {
|
|
1554
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "CAN with no open level");
|
|
1555
|
+
return len;
|
|
1556
|
+
} else {
|
|
1557
|
+
pos++;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
return pos;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/* After SYN: skip the heading's keyed units and notes. Returns the position
|
|
1564
|
+
* of the first body code. */
|
|
1565
|
+
static size_t c0__heading_end(const uint8_t *buf, size_t pos, size_t len, c0_error *err) {
|
|
1566
|
+
for (;;) {
|
|
1567
|
+
uint8_t b;
|
|
1568
|
+
pos = c0__layout(buf, pos, len);
|
|
1569
|
+
if (pos >= len) return pos;
|
|
1570
|
+
b = buf[pos];
|
|
1571
|
+
if (b == C0_SOH) {
|
|
1572
|
+
size_t p2 = c0__layout(buf, pos + 1, len), vs;
|
|
1573
|
+
if (p2 < len && buf[p2] == C0_RS) return pos; /* a header row ends the heading */
|
|
1574
|
+
pos = c0__plain(buf, p2, len);
|
|
1575
|
+
if (!(pos < len && buf[pos] == C0_US)) {
|
|
1576
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "key without a value (no US)");
|
|
1577
|
+
return len;
|
|
1578
|
+
}
|
|
1579
|
+
vs = c0__layout(buf, pos + 1, len);
|
|
1580
|
+
if (vs < len && buf[vs] == C0_ENQ) {
|
|
1581
|
+
c0__fail(err, C0_ERR_SYNTAX, vs, "reference in a heading");
|
|
1582
|
+
return len;
|
|
1583
|
+
}
|
|
1584
|
+
pos = c0__unit(buf, pos + 1, len, err);
|
|
1585
|
+
if (err->code) return len;
|
|
1586
|
+
} else if (b == C0_BEL) {
|
|
1587
|
+
pos = c0__comment(buf, pos, len, err);
|
|
1588
|
+
if (err->code) return len;
|
|
1589
|
+
} else if (b >= 0x20) {
|
|
1590
|
+
c0__fail(err, C0_ERR_SYNTAX, pos, "plain text directly after SYN");
|
|
1591
|
+
return len;
|
|
1592
|
+
} else {
|
|
1593
|
+
return pos;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
static int c0__eq_str(c0_bytes b, const char *s) {
|
|
1599
|
+
size_t n = strlen(s);
|
|
1600
|
+
return b.len == n && (n == 0 || memcmp(b.ptr, s, n) == 0);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
c0_status c0_document_read(const uint8_t *buf, size_t len, size_t start, c0_document *out, c0_error *err) {
|
|
1604
|
+
c0_error local = {C0_OK, 0, ""};
|
|
1605
|
+
size_t pos;
|
|
1606
|
+
if (!err) err = &local;
|
|
1607
|
+
c0__err_clear(err);
|
|
1608
|
+
memset(out, 0, sizeof(*out));
|
|
1609
|
+
out->buf = buf;
|
|
1610
|
+
out->start = start;
|
|
1611
|
+
pos = c0__layout(buf, start, len);
|
|
1612
|
+
out->has_syn = pos < len && buf[pos] == C0_SYN;
|
|
1613
|
+
if (out->has_syn) {
|
|
1614
|
+
pos++;
|
|
1615
|
+
out->heading_start = pos;
|
|
1616
|
+
pos = c0__heading_end(buf, pos, len, err);
|
|
1617
|
+
if (err->code) return err->code;
|
|
1618
|
+
out->heading_end = pos;
|
|
1619
|
+
} else {
|
|
1620
|
+
out->warnings |= C0_WARN_NO_SYN;
|
|
1621
|
+
out->heading_start = out->heading_end = pos;
|
|
1622
|
+
}
|
|
1623
|
+
out->body.buf = buf;
|
|
1624
|
+
out->body.start = pos;
|
|
1625
|
+
out->body.stop = c0__body_end(buf, pos, len, err);
|
|
1626
|
+
if (err->code) return err->code;
|
|
1627
|
+
pos = c0__layout(buf, out->body.stop, len);
|
|
1628
|
+
out->has_eot = pos < len && buf[pos] == C0_EOT;
|
|
1629
|
+
if (out->has_eot) {
|
|
1630
|
+
pos++;
|
|
1631
|
+
} else if (!c0__eq_str(c0_heading_shape(out), "stream")) {
|
|
1632
|
+
out->warnings |= C0_WARN_NO_EOT;
|
|
1633
|
+
}
|
|
1634
|
+
out->stop = pos;
|
|
1635
|
+
{
|
|
1636
|
+
c0_pair_iter it = c0_document_heading(out);
|
|
1637
|
+
c0_bytes k, v;
|
|
1638
|
+
while (c0_next_pair(&it, &k, &v)) {
|
|
1639
|
+
c0_bytes other;
|
|
1640
|
+
if (!c0__eq_str(k, "shape") && !c0__eq_str(k, "version") && !c0__eq_str(k, "blob")) {
|
|
1641
|
+
out->warnings |= C0_WARN_UNKNOWN_KEY;
|
|
1642
|
+
}
|
|
1643
|
+
{
|
|
1644
|
+
c0_pair_iter rest = it;
|
|
1645
|
+
c0_bytes k2;
|
|
1646
|
+
while (c0_next_pair(&rest, &k2, &other)) {
|
|
1647
|
+
if (k2.len == k.len && memcmp(k2.ptr, k.ptr, k.len) == 0) out->warnings |= C0_WARN_DUPLICATE_KEY;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
if (it.err.code) {
|
|
1652
|
+
*err = it.err;
|
|
1653
|
+
return err->code;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
return C0_OK;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
c0_pair_iter c0_document_heading(const c0_document *d) {
|
|
1660
|
+
return c0__pairs(d->buf, d->heading_start, d->heading_end);
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
c0_status c0_heading_get(const c0_document *d, const uint8_t *key, size_t klen, c0_bytes *raw) {
|
|
1664
|
+
return c0__pairs_get(c0_document_heading(d), key, klen, raw);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
c0_status c0_heading_get_str(const c0_document *d, const char *key, c0_bytes *raw) {
|
|
1668
|
+
return c0_heading_get(d, (const uint8_t *)key, strlen(key), raw);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
c0_bytes c0_heading_shape(const c0_document *d) {
|
|
1672
|
+
c0_bytes raw, v;
|
|
1673
|
+
v.ptr = d->buf;
|
|
1674
|
+
v.len = 0;
|
|
1675
|
+
if (c0_heading_get_str(d, "shape", &raw) == C0_OK && c0_value(raw, NULL, &v) != C0_OK) v.len = 0;
|
|
1676
|
+
return v;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
int c0_heading_version(const c0_document *d) {
|
|
1680
|
+
c0_bytes raw, v;
|
|
1681
|
+
int n = 0;
|
|
1682
|
+
size_t i;
|
|
1683
|
+
if (c0_heading_get_str(d, "version", &raw) != C0_OK) return 1;
|
|
1684
|
+
if (c0_value(raw, NULL, &v) != C0_OK || v.len == 0) return 1;
|
|
1685
|
+
for (i = 0; i < v.len; i++) {
|
|
1686
|
+
if (v.ptr[i] < '0' || v.ptr[i] > '9') return 1;
|
|
1687
|
+
n = n * 10 + (v.ptr[i] - '0');
|
|
1688
|
+
}
|
|
1689
|
+
return n;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
c0_status c0_heading_blob(const c0_document *d, c0_body *out) {
|
|
1693
|
+
c0_bytes raw;
|
|
1694
|
+
c0_status st = c0_heading_get_str(d, "blob", &raw);
|
|
1695
|
+
if (st) return st;
|
|
1696
|
+
return c0_unit_level(raw, out);
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
/* ---- containers -------------------------------------------------------- */
|
|
1700
|
+
|
|
1701
|
+
c0_doc_iter c0_documents(const uint8_t *buf, size_t len) {
|
|
1702
|
+
c0_doc_iter it;
|
|
1703
|
+
it.buf = buf;
|
|
1704
|
+
it.len = len;
|
|
1705
|
+
it.pos = 0;
|
|
1706
|
+
it.has_em = 0;
|
|
1707
|
+
it.em_pos = len;
|
|
1708
|
+
it.done = 0;
|
|
1709
|
+
c0__err_clear(&it.err);
|
|
1710
|
+
return it;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
int c0_next_document(c0_doc_iter *it, c0_document *out) {
|
|
1714
|
+
size_t pos;
|
|
1715
|
+
if (it->done) return 0;
|
|
1716
|
+
pos = c0__layout(it->buf, it->pos, it->len);
|
|
1717
|
+
if (pos >= it->len) {
|
|
1718
|
+
it->done = 1;
|
|
1719
|
+
return 0;
|
|
1720
|
+
}
|
|
1721
|
+
if (it->buf[pos] == C0_EM) {
|
|
1722
|
+
it->has_em = 1;
|
|
1723
|
+
it->em_pos = pos;
|
|
1724
|
+
it->done = 1;
|
|
1725
|
+
return 0;
|
|
1726
|
+
}
|
|
1727
|
+
if (it->pos > 0 && it->buf[pos] != C0_SYN) {
|
|
1728
|
+
c0__fail(&it->err, C0_ERR_SYNTAX, pos, "a document after the first must begin with SYN");
|
|
1729
|
+
it->done = 1;
|
|
1730
|
+
return 0;
|
|
1731
|
+
}
|
|
1732
|
+
if (c0_document_read(it->buf, it->len, pos, out, &it->err) != C0_OK) {
|
|
1733
|
+
it->done = 1;
|
|
1734
|
+
return 0;
|
|
1735
|
+
}
|
|
1736
|
+
it->pos = c0__layout(it->buf, out->stop, it->len);
|
|
1737
|
+
if (it->pos < it->len && it->buf[it->pos] != C0_SYN && it->buf[it->pos] != C0_EM) {
|
|
1738
|
+
c0__fail(&it->err, C0_ERR_SYNTAX, it->pos, "unexpected byte after EOT");
|
|
1739
|
+
it->done = 1;
|
|
1740
|
+
return 0;
|
|
1741
|
+
}
|
|
1742
|
+
return 1;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
c0_bytes c0_doc_iter_tail(const c0_doc_iter *it) {
|
|
1746
|
+
c0_bytes t;
|
|
1747
|
+
t.ptr = it->buf + it->len;
|
|
1748
|
+
t.len = 0;
|
|
1749
|
+
if (it->has_em) {
|
|
1750
|
+
t.ptr = it->buf + it->em_pos + 1;
|
|
1751
|
+
t.len = it->len - it->em_pos - 1;
|
|
1752
|
+
}
|
|
1753
|
+
return t;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
c0_status c0_container_tail(const uint8_t *buf, size_t len, c0_bytes *tail, int *has_em, c0_error *err) {
|
|
1757
|
+
c0_doc_iter it = c0_documents(buf, len);
|
|
1758
|
+
c0_document d;
|
|
1759
|
+
while (c0_next_document(&it, &d)) {
|
|
1760
|
+
}
|
|
1761
|
+
if (err) *err = it.err;
|
|
1762
|
+
*tail = c0_doc_iter_tail(&it);
|
|
1763
|
+
if (has_em) *has_em = it.has_em;
|
|
1764
|
+
return it.err.code;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
static int c0__to_size(c0_bytes v, size_t *out) {
|
|
1768
|
+
size_t i, n = 0;
|
|
1769
|
+
if (v.len == 0) return 0;
|
|
1770
|
+
for (i = 0; i < v.len; i++) {
|
|
1771
|
+
if (v.ptr[i] < '0' || v.ptr[i] > '9') return 0;
|
|
1772
|
+
n = n * 10 + (size_t)(v.ptr[i] - '0');
|
|
1773
|
+
}
|
|
1774
|
+
*out = n;
|
|
1775
|
+
return 1;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
c0_status c0_blob(const uint8_t *buf, size_t len, const uint8_t *name, size_t nlen, c0_bytes *out, c0_error *err) {
|
|
1779
|
+
c0_doc_iter it = c0_documents(buf, len);
|
|
1780
|
+
c0_document first;
|
|
1781
|
+
c0_body table;
|
|
1782
|
+
c0_group_iter gi;
|
|
1783
|
+
c0_group g;
|
|
1784
|
+
c0_record_iter ri;
|
|
1785
|
+
c0_record r;
|
|
1786
|
+
c0_bytes tail;
|
|
1787
|
+
size_t ni = 0, oi = 1, li = 2;
|
|
1788
|
+
int found = 0;
|
|
1789
|
+
c0_status st;
|
|
1790
|
+
if (!c0_next_document(&it, &first)) {
|
|
1791
|
+
if (err) *err = it.err;
|
|
1792
|
+
return it.err.code ? it.err.code : C0_ERR_NOT_FOUND;
|
|
1793
|
+
}
|
|
1794
|
+
{
|
|
1795
|
+
c0_document d;
|
|
1796
|
+
while (c0_next_document(&it, &d)) {
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
if (err) *err = it.err;
|
|
1800
|
+
if (it.err.code) return it.err.code;
|
|
1801
|
+
if (!it.has_em) return C0_ERR_NOT_FOUND;
|
|
1802
|
+
tail = c0_doc_iter_tail(&it);
|
|
1803
|
+
st = c0_heading_blob(&first, &table);
|
|
1804
|
+
if (st) return st;
|
|
1805
|
+
gi = c0_body_groups(table);
|
|
1806
|
+
if (!c0_next_group(&gi, &g)) return C0_ERR_NOT_FOUND;
|
|
1807
|
+
c0_group_column(g, (const uint8_t *)"name", 4, &ni);
|
|
1808
|
+
c0_group_column(g, (const uint8_t *)"offset", 6, &oi);
|
|
1809
|
+
c0_group_column(g, (const uint8_t *)"length", 6, &li);
|
|
1810
|
+
ri = c0_group_records(g);
|
|
1811
|
+
while (c0_next_record(&ri, &r)) {
|
|
1812
|
+
c0_bytes raw, v;
|
|
1813
|
+
size_t off, length;
|
|
1814
|
+
if (c0_record_field(r, ni, &raw) || c0_value(raw, NULL, &v)) continue;
|
|
1815
|
+
if (!(v.len == nlen && (nlen == 0 || memcmp(v.ptr, name, nlen) == 0))) continue;
|
|
1816
|
+
if (c0_record_field(r, oi, &raw) || c0_value(raw, NULL, &v) || !c0__to_size(v, &off)) continue;
|
|
1817
|
+
if (c0_record_field(r, li, &raw) || c0_value(raw, NULL, &v) || !c0__to_size(v, &length)) continue;
|
|
1818
|
+
if (off > tail.len || length > tail.len - off) continue;
|
|
1819
|
+
out->ptr = tail.ptr + off;
|
|
1820
|
+
out->len = length;
|
|
1821
|
+
found = 1; /* last wins */
|
|
1822
|
+
}
|
|
1823
|
+
return found ? C0_OK : C0_ERR_NOT_FOUND;
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
c0_status c0_blob_str(const uint8_t *buf, size_t len, const char *name, c0_bytes *out, c0_error *err) {
|
|
1827
|
+
return c0_blob(buf, len, (const uint8_t *)name, strlen(name), out, err);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
/* ---- builder ----------------------------------------------------------- */
|
|
1831
|
+
|
|
1832
|
+
enum { C0__B_TOP = 0, C0__B_HEADING = 1, C0__B_RECORD = 2 };
|
|
1833
|
+
|
|
1834
|
+
static int c0__b_reserve(c0_builder *b, size_t extra) {
|
|
1835
|
+
if (b->status != C0_OK) return 0;
|
|
1836
|
+
if (b->len + extra > b->cap) {
|
|
1837
|
+
size_t ncap = b->cap ? b->cap : 64;
|
|
1838
|
+
uint8_t *nd;
|
|
1839
|
+
while (ncap < b->len + extra) ncap *= 2;
|
|
1840
|
+
nd = (uint8_t *)realloc(b->data, ncap);
|
|
1841
|
+
if (!nd) {
|
|
1842
|
+
b->status = C0_ERR_OOM;
|
|
1843
|
+
return 0;
|
|
1844
|
+
}
|
|
1845
|
+
b->data = nd;
|
|
1846
|
+
b->cap = ncap;
|
|
1847
|
+
}
|
|
1848
|
+
return 1;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
static void c0__b_byte(c0_builder *b, uint8_t x) {
|
|
1852
|
+
if (c0__b_reserve(b, 1)) b->data[b->len++] = x;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
static void c0__b_raw(c0_builder *b, const uint8_t *p, size_t n) {
|
|
1856
|
+
if (n && c0__b_reserve(b, n)) {
|
|
1857
|
+
memcpy(b->data + b->len, p, n);
|
|
1858
|
+
b->len += n;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
static void c0__b_name(c0_builder *b, const uint8_t *p, size_t n) {
|
|
1863
|
+
if (!c0_is_name(p, n)) {
|
|
1864
|
+
if (b->status == C0_OK) b->status = C0_ERR_BAD_NAME;
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
c0__b_raw(b, p, n);
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
/* A value in its one spelling. */
|
|
1871
|
+
static void c0__b_value(c0_builder *b, const uint8_t *p, size_t n) {
|
|
1872
|
+
size_t i;
|
|
1873
|
+
if (c0_is_plain(p, n)) {
|
|
1874
|
+
c0__b_raw(b, p, n);
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
c0__b_byte(b, C0_STX);
|
|
1878
|
+
for (i = 0; i < n; i++) {
|
|
1879
|
+
if (c0_is_text_escaped(p[i])) c0__b_byte(b, C0_DLE);
|
|
1880
|
+
c0__b_byte(b, p[i]);
|
|
1881
|
+
}
|
|
1882
|
+
c0__b_byte(b, C0_ETX);
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
static void c0__b_end_record(c0_builder *b) {
|
|
1886
|
+
b->state = C0__B_TOP;
|
|
1887
|
+
b->units = 0;
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
/* The first value of a record, straight after RS. */
|
|
1891
|
+
static void c0__b_first_value(c0_builder *b, const uint8_t *p, size_t n) {
|
|
1892
|
+
c0__b_value(b, p, n);
|
|
1893
|
+
b->units = 1;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
/* Position for the next unit of the open record (opening one if needed):
|
|
1897
|
+
* US between units, nothing before the first. */
|
|
1898
|
+
static void c0__b_next_unit(c0_builder *b) {
|
|
1899
|
+
if (b->state != C0__B_RECORD) c0_builder_rs(b);
|
|
1900
|
+
else if (b->units > 0) c0__b_byte(b, C0_US);
|
|
1901
|
+
b->units++;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
/* SOH key US, opening a record first unless one (or the heading) is open. */
|
|
1905
|
+
static void c0__b_key_head(c0_builder *b, const uint8_t *key, size_t klen) {
|
|
1906
|
+
if (b->state == C0__B_TOP) c0_builder_rs(b);
|
|
1907
|
+
c0_builder_soh_name(b, key, klen);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
static c0_bytes c0__cstr(const char *s) {
|
|
1911
|
+
c0_bytes b;
|
|
1912
|
+
b.ptr = (const uint8_t *)s;
|
|
1913
|
+
b.len = strlen(s);
|
|
1914
|
+
return b;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
/* Build a c0_bytes array from C strings on the heap; NULL on OOM. */
|
|
1918
|
+
static c0_bytes *c0__cstrs(c0_builder *b, const char *const *strs, size_t count) {
|
|
1919
|
+
c0_bytes *out = (c0_bytes *)malloc((count ? count : 1) * sizeof(c0_bytes));
|
|
1920
|
+
size_t i;
|
|
1921
|
+
if (!out) {
|
|
1922
|
+
if (b->status == C0_OK) b->status = C0_ERR_OOM;
|
|
1923
|
+
return NULL;
|
|
1924
|
+
}
|
|
1925
|
+
for (i = 0; i < count; i++) out[i] = c0__cstr(strs[i]);
|
|
1926
|
+
return out;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
void c0_builder_init(c0_builder *b) {
|
|
1930
|
+
memset(b, 0, sizeof(*b));
|
|
1931
|
+
b->status = C0_OK;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
void c0_builder_free(c0_builder *b) {
|
|
1935
|
+
free(b->data);
|
|
1936
|
+
free(b->stack);
|
|
1937
|
+
memset(b, 0, sizeof(*b));
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
c0_status c0_builder_status(const c0_builder *b) { return b->status; }
|
|
1941
|
+
|
|
1942
|
+
c0_bytes c0_builder_bytes(const c0_builder *b) {
|
|
1943
|
+
c0_bytes r;
|
|
1944
|
+
r.ptr = b->data;
|
|
1945
|
+
r.len = b->len;
|
|
1946
|
+
return r;
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
/* ---- primitives: one function per code ---- */
|
|
1950
|
+
|
|
1951
|
+
void c0_builder_fs(c0_builder *b, const uint8_t *name, size_t nlen) {
|
|
1952
|
+
c0__b_end_record(b);
|
|
1953
|
+
c0__b_byte(b, C0_FS);
|
|
1954
|
+
c0__b_name(b, name, nlen);
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
void c0_builder_gs(c0_builder *b) {
|
|
1958
|
+
c0__b_end_record(b);
|
|
1959
|
+
c0__b_byte(b, C0_GS);
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
void c0_builder_gs_name(c0_builder *b, const uint8_t *name, size_t nlen) {
|
|
1963
|
+
c0_builder_gs(b);
|
|
1964
|
+
c0__b_name(b, name, nlen);
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
void c0_builder_rs(c0_builder *b) {
|
|
1968
|
+
c0__b_byte(b, C0_RS);
|
|
1969
|
+
b->state = C0__B_RECORD;
|
|
1970
|
+
b->units = 0;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
void c0_builder_rs_value(c0_builder *b, const uint8_t *value, size_t vlen) {
|
|
1974
|
+
c0_builder_rs(b);
|
|
1975
|
+
c0__b_first_value(b, value, vlen);
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
void c0_builder_us(c0_builder *b) {
|
|
1979
|
+
c0__b_byte(b, C0_US);
|
|
1980
|
+
b->units++;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
void c0_builder_us_value(c0_builder *b, const uint8_t *value, size_t vlen) {
|
|
1984
|
+
c0__b_byte(b, C0_US);
|
|
1985
|
+
c0__b_value(b, value, vlen);
|
|
1986
|
+
b->units++;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
/* SUB does not count as a unit: it is the new side of the unit before it. */
|
|
1990
|
+
void c0_builder_sub(c0_builder *b) { c0__b_byte(b, C0_SUB); }
|
|
1991
|
+
|
|
1992
|
+
void c0_builder_sub_value(c0_builder *b, const uint8_t *value, size_t vlen) {
|
|
1993
|
+
c0__b_byte(b, C0_SUB);
|
|
1994
|
+
c0__b_value(b, value, vlen);
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
void c0_builder_soh(c0_builder *b) { c0__b_byte(b, C0_SOH); }
|
|
1998
|
+
|
|
1999
|
+
void c0_builder_soh_name(c0_builder *b, const uint8_t *name, size_t nlen) {
|
|
2000
|
+
c0__b_byte(b, C0_SOH);
|
|
2001
|
+
c0__b_name(b, name, nlen);
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
void c0_builder_dle(c0_builder *b) {
|
|
2005
|
+
if (b->depth + 2 > b->stack_cap) {
|
|
2006
|
+
size_t ncap = b->stack_cap ? b->stack_cap * 2 : 16;
|
|
2007
|
+
int *ns = (int *)realloc(b->stack, ncap * sizeof(int) * 2);
|
|
2008
|
+
if (!ns) {
|
|
2009
|
+
if (b->status == C0_OK) b->status = C0_ERR_OOM;
|
|
2010
|
+
return;
|
|
2011
|
+
}
|
|
2012
|
+
b->stack = ns;
|
|
2013
|
+
b->stack_cap = ncap;
|
|
2014
|
+
}
|
|
2015
|
+
b->stack[2 * b->depth] = b->state;
|
|
2016
|
+
b->stack[2 * b->depth + 1] = (int)b->units;
|
|
2017
|
+
b->depth++;
|
|
2018
|
+
b->state = C0__B_TOP;
|
|
2019
|
+
b->units = 0;
|
|
2020
|
+
c0__b_byte(b, C0_DLE);
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
void c0_builder_can(c0_builder *b) {
|
|
2024
|
+
if (b->depth == 0) {
|
|
2025
|
+
if (b->status == C0_OK) b->status = C0_ERR_BAD_STATE;
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
c0__b_byte(b, C0_CAN);
|
|
2029
|
+
b->depth--;
|
|
2030
|
+
b->state = b->stack[2 * b->depth];
|
|
2031
|
+
b->units = (size_t)b->stack[2 * b->depth + 1];
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
void c0_builder_bel(c0_builder *b) { c0__b_byte(b, C0_BEL); }
|
|
2035
|
+
|
|
2036
|
+
void c0_builder_bel_text(c0_builder *b, const uint8_t *text, size_t tlen) {
|
|
2037
|
+
if (tlen == 0) { /* a note needs content: bare BEL marks the next element dead */
|
|
2038
|
+
if (b->status == C0_OK) b->status = C0_ERR_BAD_STATE;
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
c0__b_byte(b, C0_BEL);
|
|
2042
|
+
c0__b_value(b, text, tlen);
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
void c0_builder_enq(c0_builder *b, const uint8_t *name, size_t nlen) {
|
|
2046
|
+
c0__b_byte(b, C0_ENQ);
|
|
2047
|
+
if (nlen == 0 && b->status == C0_OK) b->status = C0_ERR_BAD_NAME;
|
|
2048
|
+
c0__b_name(b, name, nlen);
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
void c0_builder_enq_path(c0_builder *b, const c0_bytes *segments, size_t count) {
|
|
2052
|
+
size_t i;
|
|
2053
|
+
if (count == 1) {
|
|
2054
|
+
c0_builder_enq(b, segments[0].ptr, segments[0].len);
|
|
2055
|
+
return;
|
|
2056
|
+
}
|
|
2057
|
+
c0__b_byte(b, C0_ENQ);
|
|
2058
|
+
if ((count < 2 || count > 3) && b->status == C0_OK) b->status = C0_ERR_BAD_NAME;
|
|
2059
|
+
c0__b_byte(b, C0_STX);
|
|
2060
|
+
for (i = 0; i < count && i < 3; i++) {
|
|
2061
|
+
if (i) c0__b_byte(b, C0_US);
|
|
2062
|
+
if (segments[i].len == 0 && b->status == C0_OK) b->status = C0_ERR_BAD_NAME;
|
|
2063
|
+
c0__b_name(b, segments[i].ptr, segments[i].len);
|
|
2064
|
+
}
|
|
2065
|
+
c0__b_byte(b, C0_ETX);
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
void c0_builder_syn(c0_builder *b) {
|
|
2069
|
+
c0__b_end_record(b);
|
|
2070
|
+
c0__b_byte(b, C0_SYN);
|
|
2071
|
+
b->state = C0__B_HEADING;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
void c0_builder_eot(c0_builder *b) {
|
|
2075
|
+
c0__b_end_record(b);
|
|
2076
|
+
c0__b_byte(b, C0_EOT);
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
void c0_builder_etb(c0_builder *b) {
|
|
2080
|
+
c0__b_end_record(b);
|
|
2081
|
+
c0__b_byte(b, C0_ETB);
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
void c0_builder_etb_payload(c0_builder *b, const uint8_t *payload, size_t plen) {
|
|
2085
|
+
c0_builder_etb(b);
|
|
2086
|
+
c0__b_name(b, payload, plen);
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
void c0_builder_em(c0_builder *b, const uint8_t *tail, size_t tlen) {
|
|
2090
|
+
c0__b_end_record(b);
|
|
2091
|
+
c0__b_byte(b, C0_EM);
|
|
2092
|
+
c0__b_raw(b, tail, tlen);
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
void c0_builder_fs_str(c0_builder *b, const char *name) { c0_bytes n = c0__cstr(name); c0_builder_fs(b, n.ptr, n.len); }
|
|
2096
|
+
void c0_builder_gs_str(c0_builder *b, const char *name) { c0_bytes n = c0__cstr(name); c0_builder_gs_name(b, n.ptr, n.len); }
|
|
2097
|
+
void c0_builder_rs_str(c0_builder *b, const char *value) { c0_bytes v = c0__cstr(value); c0_builder_rs_value(b, v.ptr, v.len); }
|
|
2098
|
+
void c0_builder_us_str(c0_builder *b, const char *value) { c0_bytes v = c0__cstr(value); c0_builder_us_value(b, v.ptr, v.len); }
|
|
2099
|
+
void c0_builder_sub_str(c0_builder *b, const char *value) { c0_bytes v = c0__cstr(value); c0_builder_sub_value(b, v.ptr, v.len); }
|
|
2100
|
+
void c0_builder_soh_str(c0_builder *b, const char *name) { c0_bytes n = c0__cstr(name); c0_builder_soh_name(b, n.ptr, n.len); }
|
|
2101
|
+
void c0_builder_bel_str(c0_builder *b, const char *text) { c0_bytes t = c0__cstr(text); c0_builder_bel_text(b, t.ptr, t.len); }
|
|
2102
|
+
void c0_builder_enq_str(c0_builder *b, const char *name) { c0_bytes n = c0__cstr(name); c0_builder_enq(b, n.ptr, n.len); }
|
|
2103
|
+
void c0_builder_enq_path_str(c0_builder *b, const char *const *segments, size_t count) {
|
|
2104
|
+
c0_bytes *v = c0__cstrs(b, segments, count);
|
|
2105
|
+
if (!v) return;
|
|
2106
|
+
c0_builder_enq_path(b, v, count);
|
|
2107
|
+
free(v);
|
|
2108
|
+
}
|
|
2109
|
+
void c0_builder_etb_str(c0_builder *b, const char *payload) { c0_bytes p = c0__cstr(payload); c0_builder_etb_payload(b, p.ptr, p.len); }
|
|
2110
|
+
void c0_builder_em_str(c0_builder *b, const char *tail) { c0_bytes t = c0__cstr(tail); c0_builder_em(b, t.ptr, t.len); }
|
|
2111
|
+
|
|
2112
|
+
/* ---- composed: wrappers over the primitives ---- */
|
|
2113
|
+
|
|
2114
|
+
void c0_build_syn(c0_builder *b) { c0_builder_syn(b); }
|
|
2115
|
+
|
|
2116
|
+
void c0_build_eot(c0_builder *b) { c0_builder_eot(b); }
|
|
2117
|
+
|
|
2118
|
+
void c0_build_etb(c0_builder *b, const uint8_t *payload, size_t plen) {
|
|
2119
|
+
if (payload && plen) c0_builder_etb_payload(b, payload, plen);
|
|
2120
|
+
else c0_builder_etb(b);
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
void c0_build_em(c0_builder *b, const uint8_t *tail, size_t tlen) { c0_builder_em(b, tail, tlen); }
|
|
2124
|
+
|
|
2125
|
+
void c0_build_file(c0_builder *b, const uint8_t *name, size_t nlen) { c0_builder_fs(b, name, nlen); }
|
|
2126
|
+
|
|
2127
|
+
void c0_build_section(c0_builder *b, const uint8_t *name, size_t nlen, size_t depth) {
|
|
2128
|
+
size_t i;
|
|
2129
|
+
for (i = 1; i < depth; i++) c0_builder_gs(b);
|
|
2130
|
+
c0_builder_gs_name(b, name, nlen);
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
void c0_build_group(c0_builder *b, const uint8_t *name, size_t nlen) { c0_builder_gs_name(b, name, nlen); }
|
|
2134
|
+
|
|
2135
|
+
void c0_build_header(c0_builder *b, const c0_bytes *names, size_t count) {
|
|
2136
|
+
size_t i;
|
|
2137
|
+
for (i = 0; i < count; i++) { /* validate before writing anything */
|
|
2138
|
+
if (!c0_is_name(names[i].ptr, names[i].len)) {
|
|
2139
|
+
if (b->status == C0_OK) b->status = C0_ERR_BAD_NAME;
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
c0__b_end_record(b);
|
|
2144
|
+
c0_builder_soh(b);
|
|
2145
|
+
c0_builder_rs(b);
|
|
2146
|
+
for (i = 0; i < count; i++) {
|
|
2147
|
+
if (i) c0_builder_us(b);
|
|
2148
|
+
c0__b_raw(b, names[i].ptr, names[i].len);
|
|
2149
|
+
}
|
|
2150
|
+
c0__b_end_record(b);
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
void c0_build_record(c0_builder *b, const c0_bytes *fields, size_t count) {
|
|
2154
|
+
size_t i;
|
|
2155
|
+
c0_builder_rs(b);
|
|
2156
|
+
for (i = 0; i < count; i++) c0_build_field(b, fields[i].ptr, fields[i].len);
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
void c0_build_field(c0_builder *b, const uint8_t *value, size_t vlen) {
|
|
2160
|
+
if (b->state != C0__B_RECORD) c0_builder_rs_value(b, value, vlen);
|
|
2161
|
+
else if (b->units == 0) c0__b_first_value(b, value, vlen);
|
|
2162
|
+
else c0_builder_us_value(b, value, vlen);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
void c0_build_map(c0_builder *b, const c0_bytes *keys, const c0_bytes *values, size_t count) {
|
|
2166
|
+
size_t i;
|
|
2167
|
+
c0_builder_rs(b);
|
|
2168
|
+
for (i = 0; i < count; i++) c0_build_key(b, keys[i].ptr, keys[i].len, values[i].ptr, values[i].len);
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
void c0_build_key(c0_builder *b, const uint8_t *key, size_t klen, const uint8_t *value, size_t vlen) {
|
|
2172
|
+
c0__b_key_head(b, key, klen);
|
|
2173
|
+
c0_builder_us_value(b, value, vlen);
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
void c0_build_level_open(c0_builder *b) {
|
|
2177
|
+
c0__b_next_unit(b);
|
|
2178
|
+
c0_builder_dle(b);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
void c0_build_key_level_open(c0_builder *b, const uint8_t *key, size_t klen) {
|
|
2182
|
+
c0__b_key_head(b, key, klen);
|
|
2183
|
+
c0_builder_us(b);
|
|
2184
|
+
c0_builder_dle(b);
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
void c0_build_level_close(c0_builder *b) { c0_builder_can(b); }
|
|
2188
|
+
|
|
2189
|
+
void c0_build_list(c0_builder *b, const c0_bytes *items, size_t count) {
|
|
2190
|
+
size_t i;
|
|
2191
|
+
c0__b_next_unit(b);
|
|
2192
|
+
c0_builder_dle(b);
|
|
2193
|
+
if (count) {
|
|
2194
|
+
c0_builder_rs(b);
|
|
2195
|
+
for (i = 0; i < count; i++) c0_build_field(b, items[i].ptr, items[i].len);
|
|
2196
|
+
}
|
|
2197
|
+
c0_builder_can(b);
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
void c0_build_ref(c0_builder *b, const uint8_t *name, size_t nlen) {
|
|
2201
|
+
c0__b_next_unit(b);
|
|
2202
|
+
c0_builder_enq(b, name, nlen);
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
void c0_build_ref_path(c0_builder *b, const c0_bytes *segments, size_t count) {
|
|
2206
|
+
c0__b_next_unit(b);
|
|
2207
|
+
c0_builder_enq_path(b, segments, count);
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
void c0_build_note(c0_builder *b, const uint8_t *text, size_t tlen) { c0_builder_bel_text(b, text, tlen); }
|
|
2211
|
+
|
|
2212
|
+
void c0_build_block(c0_builder *b, const uint8_t *text, size_t tlen) {
|
|
2213
|
+
c0_bytes f;
|
|
2214
|
+
f.ptr = text;
|
|
2215
|
+
f.len = tlen;
|
|
2216
|
+
c0_build_record(b, &f, 1);
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
void c0_build_item(c0_builder *b, const uint8_t *text, size_t tlen) { c0_build_field(b, text, tlen); }
|
|
2220
|
+
|
|
2221
|
+
void c0_build_etb_str(c0_builder *b, const char *payload) {
|
|
2222
|
+
c0_bytes p = payload ? c0__cstr(payload) : c0__cstr("");
|
|
2223
|
+
c0_build_etb(b, p.ptr, p.len);
|
|
2224
|
+
}
|
|
2225
|
+
void c0_build_file_str(c0_builder *b, const char *name) { c0_bytes n = c0__cstr(name); c0_build_file(b, n.ptr, n.len); }
|
|
2226
|
+
void c0_build_group_str(c0_builder *b, const char *name) { c0_bytes n = c0__cstr(name); c0_build_group(b, n.ptr, n.len); }
|
|
2227
|
+
void c0_build_section_str(c0_builder *b, const char *name, size_t depth) { c0_bytes n = c0__cstr(name); c0_build_section(b, n.ptr, n.len, depth); }
|
|
2228
|
+
void c0_build_header_str(c0_builder *b, const char *const *names, size_t count) {
|
|
2229
|
+
c0_bytes *v = c0__cstrs(b, names, count);
|
|
2230
|
+
if (!v) return;
|
|
2231
|
+
c0_build_header(b, v, count);
|
|
2232
|
+
free(v);
|
|
2233
|
+
}
|
|
2234
|
+
void c0_build_record_str(c0_builder *b, const char *const *fields, size_t count) {
|
|
2235
|
+
c0_bytes *v = c0__cstrs(b, fields, count);
|
|
2236
|
+
if (!v) return;
|
|
2237
|
+
c0_build_record(b, v, count);
|
|
2238
|
+
free(v);
|
|
2239
|
+
}
|
|
2240
|
+
void c0_build_field_str(c0_builder *b, const char *value) { c0_bytes v = c0__cstr(value); c0_build_field(b, v.ptr, v.len); }
|
|
2241
|
+
void c0_build_map_str(c0_builder *b, const char *const *keys, const char *const *values, size_t count) {
|
|
2242
|
+
size_t i;
|
|
2243
|
+
c0_builder_rs(b);
|
|
2244
|
+
for (i = 0; i < count; i++) c0_build_key_str(b, keys[i], values[i]);
|
|
2245
|
+
}
|
|
2246
|
+
void c0_build_key_str(c0_builder *b, const char *key, const char *value) {
|
|
2247
|
+
c0_bytes k = c0__cstr(key), v = c0__cstr(value);
|
|
2248
|
+
c0_build_key(b, k.ptr, k.len, v.ptr, v.len);
|
|
2249
|
+
}
|
|
2250
|
+
void c0_build_key_level_open_str(c0_builder *b, const char *key) { c0_bytes k = c0__cstr(key); c0_build_key_level_open(b, k.ptr, k.len); }
|
|
2251
|
+
void c0_build_list_str(c0_builder *b, const char *const *items, size_t count) {
|
|
2252
|
+
c0_bytes *v = c0__cstrs(b, items, count);
|
|
2253
|
+
if (!v) return;
|
|
2254
|
+
c0_build_list(b, v, count);
|
|
2255
|
+
free(v);
|
|
2256
|
+
}
|
|
2257
|
+
void c0_build_ref_str(c0_builder *b, const char *name) { c0_bytes n = c0__cstr(name); c0_build_ref(b, n.ptr, n.len); }
|
|
2258
|
+
void c0_build_ref_path_str(c0_builder *b, const char *const *segments, size_t count) {
|
|
2259
|
+
c0_bytes *v = c0__cstrs(b, segments, count);
|
|
2260
|
+
if (!v) return;
|
|
2261
|
+
c0_build_ref_path(b, v, count);
|
|
2262
|
+
free(v);
|
|
2263
|
+
}
|
|
2264
|
+
void c0_build_note_str(c0_builder *b, const char *text) { c0_bytes t = c0__cstr(text); c0_build_note(b, t.ptr, t.len); }
|
|
2265
|
+
void c0_build_block_str(c0_builder *b, const char *text) { c0_bytes t = c0__cstr(text); c0_build_block(b, t.ptr, t.len); }
|
|
2266
|
+
void c0_build_item_str(c0_builder *b, const char *text) { c0_bytes t = c0__cstr(text); c0_build_item(b, t.ptr, t.len); }
|
|
2267
|
+
|
|
2268
|
+
/* ---- stream ------------------------------------------------------------ */
|
|
2269
|
+
|
|
2270
|
+
/* After an ETB at pos-1: skip its payload; the result is committed_end
|
|
2271
|
+
* (layout after the payload belongs to the next block). */
|
|
2272
|
+
static size_t c0__payload_end(const uint8_t *buf, size_t pos, size_t len) {
|
|
2273
|
+
size_t p = c0__layout(buf, pos, len);
|
|
2274
|
+
if (p < len && buf[p] >= 0x20) {
|
|
2275
|
+
p = c0__plain(buf, p, len);
|
|
2276
|
+
while (p > pos && buf[p - 1] == C0_SP) p--;
|
|
2277
|
+
return p;
|
|
2278
|
+
}
|
|
2279
|
+
return pos;
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
static c0_block_iter c0__blocks(const uint8_t *buf, size_t stop) {
|
|
2283
|
+
c0_block_iter it;
|
|
2284
|
+
it.buf = buf;
|
|
2285
|
+
it.stop = stop;
|
|
2286
|
+
it.pos = 0;
|
|
2287
|
+
it.block_start = 0;
|
|
2288
|
+
it.in_text = 0;
|
|
2289
|
+
it.depth = 0;
|
|
2290
|
+
return it;
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
c0_block_iter c0_stream_blocks(const c0_stream *s) { return c0__blocks(s->buf, s->committed_end); }
|
|
2294
|
+
|
|
2295
|
+
int c0_next_block(c0_block_iter *it, c0_bytes *out, int *damaged) {
|
|
2296
|
+
const uint8_t *buf = it->buf;
|
|
2297
|
+
while (it->pos < it->stop) {
|
|
2298
|
+
uint8_t b = buf[it->pos];
|
|
2299
|
+
if (it->in_text) {
|
|
2300
|
+
if (b == C0_DLE) {
|
|
2301
|
+
it->pos += 2;
|
|
2302
|
+
continue;
|
|
2303
|
+
} else if (b == C0_ETX) {
|
|
2304
|
+
it->in_text = 0;
|
|
2305
|
+
} else if (b == C0_ETB) {
|
|
2306
|
+
*out = c0__slice(buf, it->block_start, it->pos);
|
|
2307
|
+
if (damaged) *damaged = 1;
|
|
2308
|
+
it->pos = c0__payload_end(buf, it->pos + 1, it->stop);
|
|
2309
|
+
it->block_start = it->pos;
|
|
2310
|
+
it->in_text = 0;
|
|
2311
|
+
it->depth = 0;
|
|
2312
|
+
return 1;
|
|
2313
|
+
} else if (b == C0_SYN || b == C0_EOT || b == C0_EM) {
|
|
2314
|
+
it->in_text = 0;
|
|
2315
|
+
it->depth = 0;
|
|
2316
|
+
if (b == C0_SYN) it->block_start = it->pos;
|
|
2317
|
+
}
|
|
2318
|
+
it->pos++;
|
|
2319
|
+
} else {
|
|
2320
|
+
if (b == C0_STX) {
|
|
2321
|
+
it->in_text = 1;
|
|
2322
|
+
} else if (b == C0_DLE) {
|
|
2323
|
+
it->depth++;
|
|
2324
|
+
} else if (b == C0_CAN) {
|
|
2325
|
+
if (it->depth > 0) it->depth--;
|
|
2326
|
+
} else if (b == C0_ETB) {
|
|
2327
|
+
*out = c0__slice(buf, it->block_start, it->pos);
|
|
2328
|
+
if (damaged) *damaged = it->depth > 0;
|
|
2329
|
+
it->pos = c0__payload_end(buf, it->pos + 1, it->stop);
|
|
2330
|
+
it->block_start = it->pos;
|
|
2331
|
+
it->depth = 0;
|
|
2332
|
+
return 1;
|
|
2333
|
+
} else if (b == C0_SYN && it->pos > 0) {
|
|
2334
|
+
it->block_start = it->pos;
|
|
2335
|
+
it->depth = 0;
|
|
2336
|
+
}
|
|
2337
|
+
it->pos++;
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
return 0;
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
/* Is the tail after committed_end more than layout, notes, EOT or EM? */
|
|
2344
|
+
static int c0__torn_tail(const uint8_t *buf, size_t pos, size_t len) {
|
|
2345
|
+
c0_error err = {C0_OK, 0, ""};
|
|
2346
|
+
for (;;) {
|
|
2347
|
+
uint8_t b;
|
|
2348
|
+
pos = c0__layout(buf, pos, len);
|
|
2349
|
+
if (pos >= len) return 0;
|
|
2350
|
+
b = buf[pos];
|
|
2351
|
+
if (b == C0_EOT) {
|
|
2352
|
+
pos++;
|
|
2353
|
+
} else if (b == C0_EM) {
|
|
2354
|
+
return 0; /* the raw region is not data */
|
|
2355
|
+
} else if (b == C0_BEL) {
|
|
2356
|
+
if (c0__bel_kills_element(buf, pos, len)) return 1; /* a dead element is data */
|
|
2357
|
+
pos = c0__comment(buf, pos, len, &err);
|
|
2358
|
+
if (err.code) return 1;
|
|
2359
|
+
} else {
|
|
2360
|
+
return 1;
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
c0_stream c0_stream_read(const uint8_t *buf, size_t len) {
|
|
2366
|
+
c0_stream s;
|
|
2367
|
+
c0_block_iter it = c0__blocks(buf, len);
|
|
2368
|
+
c0_bytes blk;
|
|
2369
|
+
int damaged;
|
|
2370
|
+
s.buf = buf;
|
|
2371
|
+
s.len = len;
|
|
2372
|
+
s.committed_end = 0;
|
|
2373
|
+
s.block_count = 0;
|
|
2374
|
+
s.damaged_count = 0;
|
|
2375
|
+
while (c0_next_block(&it, &blk, &damaged)) {
|
|
2376
|
+
s.block_count++;
|
|
2377
|
+
if (damaged) s.damaged_count++;
|
|
2378
|
+
s.committed_end = it.pos;
|
|
2379
|
+
}
|
|
2380
|
+
s.torn = c0__torn_tail(buf, s.committed_end, len);
|
|
2381
|
+
return s;
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
c0_bytes c0_stream_committed(const c0_stream *s) { return c0__slice(s->buf, 0, s->committed_end); }
|
|
2385
|
+
c0_bytes c0_stream_tail(const c0_stream *s) { return c0__slice(s->buf, s->committed_end, s->len); }
|
|
2386
|
+
|
|
2387
|
+
size_t c0_stream_undamaged(const c0_stream *s, uint8_t *out) {
|
|
2388
|
+
c0_block_iter it = c0_stream_blocks(s);
|
|
2389
|
+
c0_bytes blk;
|
|
2390
|
+
int damaged;
|
|
2391
|
+
size_t n = 0;
|
|
2392
|
+
while (c0_next_block(&it, &blk, &damaged)) {
|
|
2393
|
+
if (damaged) continue;
|
|
2394
|
+
if (blk.len) memcpy(out + n, blk.ptr, blk.len);
|
|
2395
|
+
n += blk.len;
|
|
2396
|
+
}
|
|
2397
|
+
return n;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
size_t c0_stream_last_commit_end(const uint8_t *buf, size_t len) {
|
|
2401
|
+
size_t i = len;
|
|
2402
|
+
while (i > 0) {
|
|
2403
|
+
i--;
|
|
2404
|
+
if (buf[i] == C0_ETB) {
|
|
2405
|
+
size_t dles = 0, j = i;
|
|
2406
|
+
while (j > 0 && buf[j - 1] == C0_DLE) {
|
|
2407
|
+
dles++;
|
|
2408
|
+
j--;
|
|
2409
|
+
}
|
|
2410
|
+
if (dles % 2 == 0) return c0__plain(buf, i + 1, len);
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
return 0;
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
/* ---- validator (validator.cr) ------------------------------------------
|
|
2417
|
+
* Recursive descent over GRAMMAR.md. `c0_validate` reports the first error;
|
|
2418
|
+
* `c0_canonical` additionally applies the canonical constraints. On error
|
|
2419
|
+
* every function returns `stop` so loops end; callers test C0__V_ERR. */
|
|
2420
|
+
|
|
2421
|
+
#define C0__V_MAX_DEPTH 512
|
|
2422
|
+
|
|
2423
|
+
typedef struct {
|
|
2424
|
+
const uint8_t *buf;
|
|
2425
|
+
size_t len;
|
|
2426
|
+
int canonical;
|
|
2427
|
+
int ok;
|
|
2428
|
+
int diff;
|
|
2429
|
+
size_t depth;
|
|
2430
|
+
c0_error err;
|
|
2431
|
+
} c0__val;
|
|
2432
|
+
|
|
2433
|
+
#define C0__V_ERR(v) ((v)->err.code != C0_OK)
|
|
2434
|
+
|
|
2435
|
+
static void c0__v_nc(c0__val *v) { v->ok = 0; }
|
|
2436
|
+
|
|
2437
|
+
static size_t c0__v_layout(c0__val *v, size_t pos, size_t stop) {
|
|
2438
|
+
size_t p2 = c0__layout(v->buf, pos, stop);
|
|
2439
|
+
if (p2 != pos) c0__v_nc(v);
|
|
2440
|
+
return p2;
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
static size_t c0__v_body(c0__val *v, size_t pos, size_t stop, int depth0);
|
|
2444
|
+
static size_t c0__v_record(c0__val *v, size_t pos, size_t stop);
|
|
2445
|
+
static size_t c0__v_value(c0__val *v, size_t pos, size_t stop, int heading);
|
|
2446
|
+
static size_t c0__v_level(c0__val *v, size_t pos, size_t stop);
|
|
2447
|
+
|
|
2448
|
+
/* Keys in [start, stop) must be unique (any order). */
|
|
2449
|
+
static void c0__v_check_keys(c0__val *v, size_t start, size_t stop) {
|
|
2450
|
+
c0_pair_iter a = c0__pairs(v->buf, start, stop);
|
|
2451
|
+
c0_bytes k, k2, raw;
|
|
2452
|
+
while (c0_next_pair(&a, &k, &raw)) {
|
|
2453
|
+
c0_pair_iter rest = a;
|
|
2454
|
+
while (c0_next_pair(&rest, &k2, &raw)) {
|
|
2455
|
+
if (k2.len == k.len && (k.len == 0 || memcmp(k.ptr, k2.ptr, k.len) == 0)) c0__v_nc(v);
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
static size_t c0__v_plain_name(c0__val *v, size_t pos, size_t stop) {
|
|
2461
|
+
size_t start;
|
|
2462
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2463
|
+
if (pos < stop && v->buf[pos] == C0_STX) {
|
|
2464
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "a name cannot be text");
|
|
2465
|
+
return stop;
|
|
2466
|
+
}
|
|
2467
|
+
start = pos;
|
|
2468
|
+
pos = c0__plain(v->buf, pos, stop);
|
|
2469
|
+
if (pos > start && c0_is_layout(v->buf[pos - 1])) c0__v_nc(v);
|
|
2470
|
+
return pos;
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
/* Notes (BEL + content) only; a BEL before a code is left for the caller. */
|
|
2474
|
+
static size_t c0__v_notes(c0__val *v, size_t pos, size_t stop) {
|
|
2475
|
+
const uint8_t *buf = v->buf;
|
|
2476
|
+
for (;;) {
|
|
2477
|
+
size_t p2;
|
|
2478
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2479
|
+
if (!(pos < stop && buf[pos] == C0_BEL)) break;
|
|
2480
|
+
p2 = c0__layout(buf, pos + 1, stop);
|
|
2481
|
+
if (p2 >= stop) {
|
|
2482
|
+
c0__fail(&v->err, C0_ERR_UNEXPECTED_END, p2, "BEL at end of input");
|
|
2483
|
+
return stop;
|
|
2484
|
+
}
|
|
2485
|
+
if (buf[p2] < 0x20 && buf[p2] != C0_STX) break; /* a dead element, not a note */
|
|
2486
|
+
c0__v_nc(v);
|
|
2487
|
+
pos = buf[p2] == C0_STX ? c0__text(buf, p2, stop, &v->err) : c0__plain(buf, p2, stop);
|
|
2488
|
+
if (C0__V_ERR(v)) return stop;
|
|
2489
|
+
}
|
|
2490
|
+
return pos;
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
static size_t c0__v_trailing_notes(c0__val *v, size_t pos, size_t stop) {
|
|
2494
|
+
const uint8_t *buf = v->buf;
|
|
2495
|
+
while (pos < stop && buf[pos] == C0_BEL) {
|
|
2496
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2497
|
+
if (p2 >= stop) {
|
|
2498
|
+
c0__fail(&v->err, C0_ERR_UNEXPECTED_END, p2, "BEL at end of input");
|
|
2499
|
+
return stop;
|
|
2500
|
+
}
|
|
2501
|
+
if (!(buf[p2] >= 0x20 || buf[p2] == C0_STX)) break;
|
|
2502
|
+
c0__v_nc(v);
|
|
2503
|
+
pos = buf[p2] == C0_STX ? c0__text(buf, p2, stop, &v->err) : c0__plain(buf, p2, stop);
|
|
2504
|
+
if (C0__V_ERR(v)) return stop;
|
|
2505
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2506
|
+
}
|
|
2507
|
+
return pos;
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
static size_t c0__v_header_names(c0__val *v, size_t pos, size_t stop) {
|
|
2511
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2512
|
+
for (;;) {
|
|
2513
|
+
pos = c0__v_plain_name(v, pos, stop);
|
|
2514
|
+
if (C0__V_ERR(v)) return stop;
|
|
2515
|
+
if (!(pos < stop && v->buf[pos] == C0_US)) break;
|
|
2516
|
+
pos = c0__v_layout(v, pos + 1, stop);
|
|
2517
|
+
}
|
|
2518
|
+
return pos;
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
static size_t c0__v_header_row(c0__val *v, size_t pos, size_t stop) {
|
|
2522
|
+
size_t p2;
|
|
2523
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2524
|
+
if (!(pos < stop && v->buf[pos] == C0_SOH)) return pos;
|
|
2525
|
+
p2 = c0__v_layout(v, pos + 1, stop);
|
|
2526
|
+
if (!(p2 < stop && v->buf[p2] == C0_RS)) return pos;
|
|
2527
|
+
return c0__v_header_names(v, p2 + 1, stop);
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
static size_t c0__v_commit(c0__val *v, size_t pos, size_t stop, int depth0) {
|
|
2531
|
+
if (!depth0) {
|
|
2532
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "commit inside a nested level");
|
|
2533
|
+
return stop;
|
|
2534
|
+
}
|
|
2535
|
+
c0__v_nc(v);
|
|
2536
|
+
return c0__plain(v->buf, c0__v_layout(v, pos + 1, stop), stop);
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
/* Before a header row: notes, commits (depth zero), dead records, dead header rows. */
|
|
2540
|
+
static size_t c0__v_pre(c0__val *v, size_t pos, size_t stop, int depth0) {
|
|
2541
|
+
const uint8_t *buf = v->buf;
|
|
2542
|
+
for (;;) {
|
|
2543
|
+
uint8_t b;
|
|
2544
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2545
|
+
if (pos >= stop || C0__V_ERR(v)) break;
|
|
2546
|
+
b = buf[pos];
|
|
2547
|
+
if (b == C0_ETB) {
|
|
2548
|
+
pos = c0__v_commit(v, pos, stop, depth0);
|
|
2549
|
+
} else if (b == C0_BEL) {
|
|
2550
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2551
|
+
if (p2 >= stop) {
|
|
2552
|
+
c0__fail(&v->err, C0_ERR_UNEXPECTED_END, p2, "BEL at end of input");
|
|
2553
|
+
return stop;
|
|
2554
|
+
}
|
|
2555
|
+
if (buf[p2] >= 0x20 || buf[p2] == C0_STX) {
|
|
2556
|
+
pos = c0__v_notes(v, pos, stop);
|
|
2557
|
+
} else if (buf[p2] == C0_RS) {
|
|
2558
|
+
c0__v_nc(v);
|
|
2559
|
+
pos = c0__v_record(v, p2 + 1, stop);
|
|
2560
|
+
} else if (buf[p2] == C0_SOH) {
|
|
2561
|
+
size_t p3 = c0__layout(buf, p2 + 1, stop);
|
|
2562
|
+
if (p3 < stop && buf[p3] == C0_RS) {
|
|
2563
|
+
c0__v_nc(v);
|
|
2564
|
+
pos = c0__v_header_names(v, p3 + 1, stop);
|
|
2565
|
+
} else {
|
|
2566
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, p2, "keyed unit outside a record");
|
|
2567
|
+
return stop;
|
|
2568
|
+
}
|
|
2569
|
+
} else {
|
|
2570
|
+
break;
|
|
2571
|
+
}
|
|
2572
|
+
} else {
|
|
2573
|
+
break;
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
return pos;
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
static size_t c0__v_items(c0__val *v, size_t pos, size_t stop, int depth0) {
|
|
2580
|
+
const uint8_t *buf = v->buf;
|
|
2581
|
+
for (;;) {
|
|
2582
|
+
uint8_t b;
|
|
2583
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2584
|
+
if (pos >= stop || C0__V_ERR(v)) break;
|
|
2585
|
+
b = buf[pos];
|
|
2586
|
+
if (b == C0_RS) {
|
|
2587
|
+
pos = c0__v_record(v, pos + 1, stop);
|
|
2588
|
+
} else if (b == C0_ETB) {
|
|
2589
|
+
pos = c0__v_commit(v, pos, stop, depth0);
|
|
2590
|
+
} else if (b == C0_BEL) {
|
|
2591
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2592
|
+
if (p2 >= stop || buf[p2] >= 0x20 || buf[p2] == C0_STX) {
|
|
2593
|
+
pos = c0__v_notes(v, pos, stop);
|
|
2594
|
+
} else if (buf[p2] == C0_RS) {
|
|
2595
|
+
c0__v_nc(v);
|
|
2596
|
+
pos = c0__v_record(v, p2 + 1, stop);
|
|
2597
|
+
} else if (buf[p2] == C0_SOH) {
|
|
2598
|
+
size_t p3 = c0__layout(buf, p2 + 1, stop);
|
|
2599
|
+
if (p3 < stop && buf[p3] == C0_RS) {
|
|
2600
|
+
c0__v_nc(v);
|
|
2601
|
+
pos = c0__v_header_names(v, p3 + 1, stop);
|
|
2602
|
+
} else {
|
|
2603
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, p2, "keyed unit outside a record");
|
|
2604
|
+
return stop;
|
|
2605
|
+
}
|
|
2606
|
+
} else if (buf[p2] == C0_DLE) {
|
|
2607
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, p2, "a nested level is not an item");
|
|
2608
|
+
return stop;
|
|
2609
|
+
} else {
|
|
2610
|
+
break; /* a dead GS or FS: the caller's business */
|
|
2611
|
+
}
|
|
2612
|
+
} else if (b == C0_SOH) {
|
|
2613
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2614
|
+
if (p2 < stop && buf[p2] == C0_RS) {
|
|
2615
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "header row not at the start of its group");
|
|
2616
|
+
} else {
|
|
2617
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "keyed unit outside a record");
|
|
2618
|
+
}
|
|
2619
|
+
return stop;
|
|
2620
|
+
} else {
|
|
2621
|
+
break;
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
return pos;
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
static size_t c0__v_group(c0__val *v, size_t pos, size_t stop, int depth0) {
|
|
2628
|
+
while (pos < stop && v->buf[pos] == C0_GS) pos = c0__v_layout(v, pos + 1, stop);
|
|
2629
|
+
pos = c0__v_plain_name(v, pos, stop);
|
|
2630
|
+
if (C0__V_ERR(v)) return stop;
|
|
2631
|
+
pos = c0__v_pre(v, pos, stop, depth0);
|
|
2632
|
+
pos = c0__v_header_row(v, pos, stop);
|
|
2633
|
+
return c0__v_items(v, pos, stop, depth0);
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
static size_t c0__v_part(c0__val *v, size_t pos, size_t stop, int depth0) {
|
|
2637
|
+
const uint8_t *buf = v->buf;
|
|
2638
|
+
pos = c0__v_pre(v, pos, stop, depth0);
|
|
2639
|
+
pos = c0__v_header_row(v, pos, stop);
|
|
2640
|
+
pos = c0__v_items(v, pos, stop, depth0);
|
|
2641
|
+
while (pos < stop && !C0__V_ERR(v)) {
|
|
2642
|
+
uint8_t b = buf[pos];
|
|
2643
|
+
if (b == C0_GS) {
|
|
2644
|
+
pos = c0__v_group(v, pos, stop, depth0);
|
|
2645
|
+
} else if (b == C0_BEL) {
|
|
2646
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2647
|
+
if (p2 < stop && buf[p2] == C0_GS) {
|
|
2648
|
+
c0__v_nc(v);
|
|
2649
|
+
pos = c0__v_group(v, p2, stop, depth0);
|
|
2650
|
+
} else {
|
|
2651
|
+
break;
|
|
2652
|
+
}
|
|
2653
|
+
} else {
|
|
2654
|
+
break;
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
return pos;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
static size_t c0__v_diff_body(c0__val *v, size_t pos, size_t stop);
|
|
2661
|
+
|
|
2662
|
+
static size_t c0__v_body(c0__val *v, size_t pos, size_t stop, int depth0) {
|
|
2663
|
+
const uint8_t *buf = v->buf;
|
|
2664
|
+
if (v->diff && depth0) return c0__v_diff_body(v, pos, stop);
|
|
2665
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2666
|
+
pos = c0__v_part(v, pos, stop, depth0);
|
|
2667
|
+
while (pos < stop && !C0__V_ERR(v)) {
|
|
2668
|
+
uint8_t b = buf[pos];
|
|
2669
|
+
if (b == C0_FS) {
|
|
2670
|
+
pos = c0__v_plain_name(v, pos + 1, stop);
|
|
2671
|
+
pos = c0__v_part(v, pos, stop, depth0);
|
|
2672
|
+
} else if (b == C0_BEL) {
|
|
2673
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2674
|
+
if (p2 < stop && buf[p2] == C0_FS) {
|
|
2675
|
+
c0__v_nc(v);
|
|
2676
|
+
pos = c0__v_plain_name(v, p2 + 1, stop);
|
|
2677
|
+
pos = c0__v_part(v, pos, stop, depth0);
|
|
2678
|
+
} else {
|
|
2679
|
+
break;
|
|
2680
|
+
}
|
|
2681
|
+
} else {
|
|
2682
|
+
break;
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
return C0__V_ERR(v) ? stop : pos;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
/* Does the record starting here begin with a (live or dead) keyed unit? */
|
|
2689
|
+
static int c0__v_keyed_start(c0__val *v, size_t pos, size_t stop) {
|
|
2690
|
+
const uint8_t *buf = v->buf;
|
|
2691
|
+
c0_error scratch = {C0_OK, 0, ""};
|
|
2692
|
+
while (pos < stop) {
|
|
2693
|
+
size_t p2;
|
|
2694
|
+
uint8_t b = buf[pos];
|
|
2695
|
+
if (b == C0_SOH) return 1;
|
|
2696
|
+
if (b != C0_BEL) return 0;
|
|
2697
|
+
p2 = c0__layout(buf, pos + 1, stop);
|
|
2698
|
+
if (p2 >= stop) return 0;
|
|
2699
|
+
if (buf[p2] == C0_SOH) return 1;
|
|
2700
|
+
if (buf[p2] < 0x20 && buf[p2] != C0_STX) return 0;
|
|
2701
|
+
pos = c0__layout(buf, c0__comment(buf, pos, stop, &scratch), stop);
|
|
2702
|
+
if (scratch.code) return 0;
|
|
2703
|
+
}
|
|
2704
|
+
return 0;
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
/* After an SOH that is not a header row. */
|
|
2708
|
+
static size_t c0__v_keyed_unit(c0__val *v, size_t pos, size_t stop, int heading) {
|
|
2709
|
+
const uint8_t *buf = v->buf;
|
|
2710
|
+
size_t ks, ke, vs;
|
|
2711
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2712
|
+
ks = pos;
|
|
2713
|
+
ke = pos = c0__plain(buf, pos, stop);
|
|
2714
|
+
if (!(pos < stop && buf[pos] == C0_US)) {
|
|
2715
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "key without a value (no US)");
|
|
2716
|
+
return stop;
|
|
2717
|
+
}
|
|
2718
|
+
vs = pos + 1;
|
|
2719
|
+
pos = c0__v_value(v, vs, stop, heading);
|
|
2720
|
+
if (heading && !C0__V_ERR(v)) {
|
|
2721
|
+
c0_bytes key = c0_trim(c0__slice(buf, ks, ke));
|
|
2722
|
+
c0_bytes val = c0_trim(c0__slice(buf, vs, pos));
|
|
2723
|
+
if (c0__eq_str(key, "shape") && c0__eq_str(val, "diff")) v->diff = 1;
|
|
2724
|
+
}
|
|
2725
|
+
return pos;
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
static size_t c0__v_record(c0__val *v, size_t pos, size_t stop) {
|
|
2729
|
+
const uint8_t *buf = v->buf;
|
|
2730
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2731
|
+
if (c0__v_keyed_start(v, pos, stop)) {
|
|
2732
|
+
size_t rec_start = pos;
|
|
2733
|
+
for (;;) {
|
|
2734
|
+
uint8_t b;
|
|
2735
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2736
|
+
if (pos >= stop || C0__V_ERR(v)) break;
|
|
2737
|
+
b = buf[pos];
|
|
2738
|
+
if (b == C0_SOH) {
|
|
2739
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2740
|
+
if (p2 < stop && buf[p2] == C0_RS) break;
|
|
2741
|
+
pos = c0__v_keyed_unit(v, pos + 1, stop, 0);
|
|
2742
|
+
} else if (b == C0_BEL) {
|
|
2743
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2744
|
+
if (p2 >= stop) {
|
|
2745
|
+
c0__fail(&v->err, C0_ERR_UNEXPECTED_END, p2, "BEL at end of input");
|
|
2746
|
+
return stop;
|
|
2747
|
+
}
|
|
2748
|
+
if (buf[p2] == C0_SOH) {
|
|
2749
|
+
size_t p3 = c0__layout(buf, p2 + 1, stop);
|
|
2750
|
+
if (p3 < stop && buf[p3] == C0_RS) break;
|
|
2751
|
+
c0__v_nc(v);
|
|
2752
|
+
pos = c0__v_keyed_unit(v, p2 + 1, stop, 0);
|
|
2753
|
+
} else if (buf[p2] >= 0x20 || buf[p2] == C0_STX) {
|
|
2754
|
+
pos = c0__v_notes(v, pos, stop);
|
|
2755
|
+
} else {
|
|
2756
|
+
break;
|
|
2757
|
+
}
|
|
2758
|
+
} else {
|
|
2759
|
+
break;
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
if (C0__V_ERR(v)) return stop;
|
|
2763
|
+
c0__v_check_keys(v, rec_start, pos);
|
|
2764
|
+
if (pos < stop && buf[pos] == C0_US) {
|
|
2765
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "US after a keyed value (mixed record)");
|
|
2766
|
+
return stop;
|
|
2767
|
+
}
|
|
2768
|
+
return pos;
|
|
2769
|
+
}
|
|
2770
|
+
for (;;) {
|
|
2771
|
+
uint8_t b;
|
|
2772
|
+
pos = c0__v_value(v, pos, stop, 0);
|
|
2773
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2774
|
+
if (pos >= stop || C0__V_ERR(v)) break;
|
|
2775
|
+
b = buf[pos];
|
|
2776
|
+
if (b == C0_US) {
|
|
2777
|
+
pos++;
|
|
2778
|
+
} else if (b == C0_SOH) {
|
|
2779
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2780
|
+
if (p2 < stop && buf[p2] == C0_RS) break;
|
|
2781
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "keyed unit in a positional record (mixed record)");
|
|
2782
|
+
return stop;
|
|
2783
|
+
} else {
|
|
2784
|
+
break;
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
return C0__V_ERR(v) ? stop : pos;
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
/* Would the decoded inside of a text be a plain value? */
|
|
2791
|
+
static int c0__v_inside_plain(const uint8_t *p, size_t n) {
|
|
2792
|
+
size_t i;
|
|
2793
|
+
if (n == 0) return 0;
|
|
2794
|
+
for (i = 0; i < n; i++) {
|
|
2795
|
+
if (p[i] < 0x20) return 0; /* a control byte, or a DLE escaping one */
|
|
2796
|
+
}
|
|
2797
|
+
return !c0_is_layout(p[0]) && !c0_is_layout(p[n - 1]);
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
static int c0__v_second_value(const uint8_t *buf, size_t after, size_t stop) {
|
|
2801
|
+
return after < stop && (buf[after] >= 0x20 || buf[after] == C0_STX || buf[after] == C0_DLE || buf[after] == C0_ENQ);
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
static size_t c0__v_value(c0__val *v, size_t pos, size_t stop, int heading) {
|
|
2805
|
+
const uint8_t *buf = v->buf;
|
|
2806
|
+
size_t after;
|
|
2807
|
+
uint8_t b;
|
|
2808
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2809
|
+
if (pos >= stop) return pos;
|
|
2810
|
+
b = buf[pos];
|
|
2811
|
+
if (b >= 0x20) {
|
|
2812
|
+
pos = c0__plain(buf, pos, stop);
|
|
2813
|
+
if (c0_is_layout(buf[pos - 1])) c0__v_nc(v);
|
|
2814
|
+
} else if (b == C0_STX) {
|
|
2815
|
+
size_t start = pos;
|
|
2816
|
+
pos = c0__text(buf, pos, stop, &v->err);
|
|
2817
|
+
if (C0__V_ERR(v)) return stop;
|
|
2818
|
+
if (v->canonical) {
|
|
2819
|
+
size_t n = pos - start - 2;
|
|
2820
|
+
if (n == 0 || c0__v_inside_plain(buf + start + 1, n)) c0__v_nc(v); /* empty or gratuitous text */
|
|
2821
|
+
}
|
|
2822
|
+
} else if (b == C0_DLE) {
|
|
2823
|
+
pos = c0__v_level(v, pos, stop);
|
|
2824
|
+
if (C0__V_ERR(v)) return stop;
|
|
2825
|
+
} else if (b == C0_ENQ) {
|
|
2826
|
+
if (heading) {
|
|
2827
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "reference in a heading");
|
|
2828
|
+
return stop;
|
|
2829
|
+
}
|
|
2830
|
+
pos = c0__v_layout(v, pos + 1, stop);
|
|
2831
|
+
if (pos >= stop || (buf[pos] < 0x20 && buf[pos] != C0_STX)) {
|
|
2832
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "reference without a name");
|
|
2833
|
+
return stop;
|
|
2834
|
+
}
|
|
2835
|
+
if (buf[pos] == C0_STX) {
|
|
2836
|
+
c0_ref r;
|
|
2837
|
+
size_t start = pos;
|
|
2838
|
+
pos = c0__text(buf, pos, stop, &v->err);
|
|
2839
|
+
if (C0__V_ERR(v)) return stop;
|
|
2840
|
+
if (c0_unit_ref(c0__slice(buf, start - 1, pos), &r) != C0_OK) {
|
|
2841
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, start, "reference path with an empty, extra or non-name segment");
|
|
2842
|
+
return stop;
|
|
2843
|
+
}
|
|
2844
|
+
if (r.count == 1) c0__v_nc(v); /* group reference written as text */
|
|
2845
|
+
} else {
|
|
2846
|
+
pos = c0__plain(buf, pos, stop);
|
|
2847
|
+
}
|
|
2848
|
+
return c0__v_trailing_notes(v, c0__v_layout(v, pos, stop), stop);
|
|
2849
|
+
} else if (b == C0_BEL) {
|
|
2850
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2851
|
+
if (p2 >= stop) {
|
|
2852
|
+
c0__fail(&v->err, C0_ERR_UNEXPECTED_END, p2, "BEL at end of input");
|
|
2853
|
+
return stop;
|
|
2854
|
+
}
|
|
2855
|
+
if (buf[p2] == C0_DLE) {
|
|
2856
|
+
c0__v_nc(v);
|
|
2857
|
+
pos = c0__v_level(v, p2, stop);
|
|
2858
|
+
} else if (buf[p2] < 0x20 && buf[p2] != C0_STX) {
|
|
2859
|
+
return pos; /* a dead record-level element: the caller's */
|
|
2860
|
+
} else {
|
|
2861
|
+
c0__v_nc(v);
|
|
2862
|
+
pos = buf[p2] == C0_STX ? c0__text(buf, p2, stop, &v->err) : c0__plain(buf, p2, stop);
|
|
2863
|
+
}
|
|
2864
|
+
if (C0__V_ERR(v)) return stop;
|
|
2865
|
+
return c0__v_layout(v, pos, stop);
|
|
2866
|
+
} else {
|
|
2867
|
+
return pos;
|
|
2868
|
+
}
|
|
2869
|
+
after = c0__v_trailing_notes(v, c0__v_layout(v, pos, stop), stop);
|
|
2870
|
+
if (C0__V_ERR(v)) return stop;
|
|
2871
|
+
if (c0__v_second_value(buf, after, stop)) {
|
|
2872
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, after, "two values in one unit");
|
|
2873
|
+
return stop;
|
|
2874
|
+
}
|
|
2875
|
+
return after;
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2878
|
+
static size_t c0__v_level(c0__val *v, size_t pos, size_t stop) {
|
|
2879
|
+
const uint8_t *buf = v->buf;
|
|
2880
|
+
uint8_t b;
|
|
2881
|
+
if (++v->depth > C0__V_MAX_DEPTH) {
|
|
2882
|
+
c0__fail(&v->err, C0_ERR_TOO_DEEP, pos, "levels nested too deep");
|
|
2883
|
+
return stop;
|
|
2884
|
+
}
|
|
2885
|
+
pos = c0__v_layout(v, pos + 1, stop);
|
|
2886
|
+
if (pos >= stop) {
|
|
2887
|
+
c0__fail(&v->err, C0_ERR_UNEXPECTED_END, pos, "level not closed");
|
|
2888
|
+
return stop;
|
|
2889
|
+
}
|
|
2890
|
+
b = buf[pos];
|
|
2891
|
+
if (b == C0_CAN) {
|
|
2892
|
+
v->depth--;
|
|
2893
|
+
return pos + 1;
|
|
2894
|
+
}
|
|
2895
|
+
if (!(b == C0_FS || b == C0_GS || b == C0_RS || b == C0_SOH || b == C0_BEL)) {
|
|
2896
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "DLE must be followed by a structural code");
|
|
2897
|
+
return stop;
|
|
2898
|
+
}
|
|
2899
|
+
pos = c0__v_body(v, pos, stop, 0);
|
|
2900
|
+
if (C0__V_ERR(v)) return stop;
|
|
2901
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2902
|
+
if (pos >= stop) {
|
|
2903
|
+
c0__fail(&v->err, C0_ERR_UNEXPECTED_END, pos, "level not closed");
|
|
2904
|
+
return stop;
|
|
2905
|
+
}
|
|
2906
|
+
if (buf[pos] != C0_CAN) {
|
|
2907
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "unexpected byte inside level");
|
|
2908
|
+
return stop;
|
|
2909
|
+
}
|
|
2910
|
+
v->depth--;
|
|
2911
|
+
return pos + 1;
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
/* After SYN: keyed units and notes. Returns the position of the first body code. */
|
|
2915
|
+
static size_t c0__v_heading(c0__val *v, size_t pos) {
|
|
2916
|
+
const uint8_t *buf = v->buf;
|
|
2917
|
+
size_t stop = v->len, hs;
|
|
2918
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2919
|
+
hs = pos;
|
|
2920
|
+
while (pos < stop && !C0__V_ERR(v)) {
|
|
2921
|
+
uint8_t b = buf[pos];
|
|
2922
|
+
if (b == C0_SOH) {
|
|
2923
|
+
size_t p2 = c0__v_layout(v, pos + 1, stop);
|
|
2924
|
+
if (p2 < stop && buf[p2] == C0_RS) break;
|
|
2925
|
+
pos = c0__v_keyed_unit(v, pos + 1, stop, 1);
|
|
2926
|
+
} else if (b == C0_BEL) {
|
|
2927
|
+
c0__v_nc(v);
|
|
2928
|
+
pos = c0__comment(buf, pos, stop, &v->err);
|
|
2929
|
+
} else if (b >= 0x20) {
|
|
2930
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "plain text directly after SYN");
|
|
2931
|
+
return stop;
|
|
2932
|
+
} else {
|
|
2933
|
+
break;
|
|
2934
|
+
}
|
|
2935
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2936
|
+
}
|
|
2937
|
+
if (C0__V_ERR(v)) return stop;
|
|
2938
|
+
c0__v_check_keys(v, hs, pos);
|
|
2939
|
+
return pos;
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
/* ---- diff body ---- */
|
|
2943
|
+
|
|
2944
|
+
static size_t c0__v_dvalue(c0__val *v, size_t pos, size_t stop) {
|
|
2945
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2946
|
+
if (pos >= stop) return pos;
|
|
2947
|
+
if (v->buf[pos] == C0_STX) {
|
|
2948
|
+
pos = c0__text(v->buf, pos, stop, &v->err);
|
|
2949
|
+
if (C0__V_ERR(v)) return stop;
|
|
2950
|
+
} else if (v->buf[pos] >= 0x20) {
|
|
2951
|
+
pos = c0__plain(v->buf, pos, stop);
|
|
2952
|
+
}
|
|
2953
|
+
return c0__v_layout(v, pos, stop);
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
static size_t c0__v_pattern(c0__val *v, size_t pos, size_t stop) {
|
|
2957
|
+
for (;;) {
|
|
2958
|
+
pos = c0__v_dvalue(v, pos, stop);
|
|
2959
|
+
if (pos < stop && v->buf[pos] == C0_SUB) pos = c0__v_dvalue(v, pos + 1, stop);
|
|
2960
|
+
pos = c0__v_trailing_notes(v, pos, stop);
|
|
2961
|
+
if (C0__V_ERR(v)) return stop;
|
|
2962
|
+
if (!(pos < stop && v->buf[pos] == C0_US)) break;
|
|
2963
|
+
pos++;
|
|
2964
|
+
}
|
|
2965
|
+
return pos;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
static size_t c0__v_replace(c0__val *v, size_t pos, size_t stop) {
|
|
2969
|
+
size_t start = c0__layout(v->buf, pos, stop);
|
|
2970
|
+
pos = c0__v_dvalue(v, pos, stop);
|
|
2971
|
+
if (C0__V_ERR(v)) return stop;
|
|
2972
|
+
if (c0_trim(c0__slice(v->buf, start, pos)).len == 0) {
|
|
2973
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, start, "replace-all with an empty old part");
|
|
2974
|
+
return stop;
|
|
2975
|
+
}
|
|
2976
|
+
if (!(pos < stop && v->buf[pos] == C0_SUB)) {
|
|
2977
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "replace-all without SUB");
|
|
2978
|
+
return stop;
|
|
2979
|
+
}
|
|
2980
|
+
return c0__v_dvalue(v, pos + 1, stop);
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
static size_t c0__v_diff_body(c0__val *v, size_t pos, size_t stop) {
|
|
2984
|
+
const uint8_t *buf = v->buf;
|
|
2985
|
+
pos = c0__v_layout(v, pos, stop);
|
|
2986
|
+
while (pos < stop && !C0__V_ERR(v)) {
|
|
2987
|
+
uint8_t b = buf[pos];
|
|
2988
|
+
if (b == C0_FS) {
|
|
2989
|
+
pos = c0__v_plain_name(v, pos + 1, stop);
|
|
2990
|
+
} else if (b == C0_GS) {
|
|
2991
|
+
pos = c0__v_pattern(v, pos + 1, stop);
|
|
2992
|
+
} else if (b == C0_RS) {
|
|
2993
|
+
pos = c0__v_replace(v, pos + 1, stop);
|
|
2994
|
+
} else if (b == C0_BEL) {
|
|
2995
|
+
size_t p2 = c0__layout(buf, pos + 1, stop);
|
|
2996
|
+
if (p2 < stop && buf[p2] == C0_FS) {
|
|
2997
|
+
pos = c0__v_plain_name(v, p2 + 1, stop);
|
|
2998
|
+
} else if (p2 < stop && buf[p2] == C0_GS) {
|
|
2999
|
+
pos = c0__v_pattern(v, p2 + 1, stop);
|
|
3000
|
+
} else if (p2 < stop && buf[p2] == C0_RS) {
|
|
3001
|
+
pos = c0__v_replace(v, p2 + 1, stop);
|
|
3002
|
+
} else {
|
|
3003
|
+
pos = c0__v_notes(v, pos, stop);
|
|
3004
|
+
}
|
|
3005
|
+
} else if (b == C0_ETB) {
|
|
3006
|
+
c0__v_nc(v);
|
|
3007
|
+
pos = c0__plain(buf, c0__v_layout(v, pos + 1, stop), stop);
|
|
3008
|
+
} else if (b == C0_EOT || b == C0_SYN || b == C0_EM) {
|
|
3009
|
+
break;
|
|
3010
|
+
} else {
|
|
3011
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "unexpected byte in a diff body");
|
|
3012
|
+
return stop;
|
|
3013
|
+
}
|
|
3014
|
+
pos = c0__v_layout(v, pos, stop);
|
|
3015
|
+
}
|
|
3016
|
+
return C0__V_ERR(v) ? stop : pos;
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
static void c0__v_run(c0__val *v) {
|
|
3020
|
+
const uint8_t *buf = v->buf;
|
|
3021
|
+
size_t len = v->len, pos;
|
|
3022
|
+
int first = 1;
|
|
3023
|
+
pos = c0__v_layout(v, 0, len);
|
|
3024
|
+
while (pos < len && !C0__V_ERR(v)) {
|
|
3025
|
+
uint8_t b = buf[pos];
|
|
3026
|
+
if (b == C0_SYN) {
|
|
3027
|
+
c0__v_nc(v);
|
|
3028
|
+
pos = c0__v_heading(v, pos + 1);
|
|
3029
|
+
first = 1;
|
|
3030
|
+
} else if (b == C0_EM) {
|
|
3031
|
+
c0__v_nc(v);
|
|
3032
|
+
return; /* the raw region follows */
|
|
3033
|
+
} else {
|
|
3034
|
+
if (!first) {
|
|
3035
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "a document after the first must begin with SYN");
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
pos = c0__v_body(v, pos, len, 1);
|
|
3039
|
+
if (C0__V_ERR(v)) return;
|
|
3040
|
+
pos = c0__v_layout(v, pos, len);
|
|
3041
|
+
first = 0;
|
|
3042
|
+
if (pos < len) {
|
|
3043
|
+
b = buf[pos];
|
|
3044
|
+
if (b == C0_EOT) {
|
|
3045
|
+
c0__v_nc(v);
|
|
3046
|
+
pos = c0__v_layout(v, pos + 1, len);
|
|
3047
|
+
if (pos < len && buf[pos] != C0_SYN && buf[pos] != C0_EM) {
|
|
3048
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "unexpected byte after EOT");
|
|
3049
|
+
return;
|
|
3050
|
+
}
|
|
3051
|
+
} else if (b == C0_SYN || b == C0_EM) {
|
|
3052
|
+
continue;
|
|
3053
|
+
} else if (b < 0x20 && !c0__assigned[b]) {
|
|
3054
|
+
c0__fail(&v->err, C0_ERR_UNASSIGNED, pos, "unassigned control code");
|
|
3055
|
+
return;
|
|
3056
|
+
} else {
|
|
3057
|
+
c0__fail(&v->err, C0_ERR_SYNTAX, pos, "unexpected byte after document");
|
|
3058
|
+
return;
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
static void c0__v_init(c0__val *v, const uint8_t *buf, size_t len, int canonical) {
|
|
3066
|
+
v->buf = buf;
|
|
3067
|
+
v->len = len;
|
|
3068
|
+
v->canonical = canonical;
|
|
3069
|
+
v->ok = 1;
|
|
3070
|
+
v->diff = 0;
|
|
3071
|
+
v->depth = 0;
|
|
3072
|
+
c0__err_clear(&v->err);
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
c0_status c0_validate(const uint8_t *buf, size_t len, c0_error *err) {
|
|
3076
|
+
c0__val v;
|
|
3077
|
+
c0__v_init(&v, buf, len, 0);
|
|
3078
|
+
c0__v_run(&v);
|
|
3079
|
+
if (err) *err = v.err;
|
|
3080
|
+
return v.err.code;
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
int c0_canonical(const uint8_t *buf, size_t len) {
|
|
3084
|
+
c0__val v;
|
|
3085
|
+
c0__v_init(&v, buf, len, 1);
|
|
3086
|
+
c0__v_run(&v);
|
|
3087
|
+
return v.err.code == C0_OK && v.ok;
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
/* ---- pretty form (pretty.cr) ------------------------------------------- */
|
|
3091
|
+
|
|
3092
|
+
typedef struct {
|
|
3093
|
+
uint8_t *d;
|
|
3094
|
+
size_t n;
|
|
3095
|
+
size_t cap;
|
|
3096
|
+
int oom;
|
|
3097
|
+
} c0__sb;
|
|
3098
|
+
|
|
3099
|
+
static int c0__sb_reserve(c0__sb *s, size_t extra) {
|
|
3100
|
+
if (s->oom) return 0;
|
|
3101
|
+
if (s->n + extra > s->cap) {
|
|
3102
|
+
size_t ncap = s->cap ? s->cap : 64;
|
|
3103
|
+
uint8_t *nd;
|
|
3104
|
+
while (ncap < s->n + extra) ncap *= 2;
|
|
3105
|
+
nd = (uint8_t *)realloc(s->d, ncap);
|
|
3106
|
+
if (!nd) {
|
|
3107
|
+
s->oom = 1;
|
|
3108
|
+
return 0;
|
|
3109
|
+
}
|
|
3110
|
+
s->d = nd;
|
|
3111
|
+
s->cap = ncap;
|
|
3112
|
+
}
|
|
3113
|
+
return 1;
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
static void c0__sb_byte(c0__sb *s, uint8_t b) {
|
|
3117
|
+
if (c0__sb_reserve(s, 1)) s->d[s->n++] = b;
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
static void c0__sb_raw(c0__sb *s, const uint8_t *p, size_t n) {
|
|
3121
|
+
if (n && c0__sb_reserve(s, n)) {
|
|
3122
|
+
memcpy(s->d + s->n, p, n);
|
|
3123
|
+
s->n += n;
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
static void c0__sb_cstr(c0__sb *s, const char *p) { c0__sb_raw(s, (const uint8_t *)p, strlen(p)); }
|
|
3128
|
+
|
|
3129
|
+
/* U+2400 + byte as 3-byte UTF-8. */
|
|
3130
|
+
static void c0__sb_glyph(c0__sb *s, uint8_t byte) {
|
|
3131
|
+
c0__sb_byte(s, 0xE2);
|
|
3132
|
+
c0__sb_byte(s, 0x90);
|
|
3133
|
+
c0__sb_byte(s, (uint8_t)(0x80 | byte));
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
static void c0__sb_spaces(c0__sb *s, size_t n) {
|
|
3137
|
+
while (n--) c0__sb_byte(s, ' ');
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
typedef struct {
|
|
3141
|
+
const uint8_t *buf;
|
|
3142
|
+
size_t len;
|
|
3143
|
+
c0__sb *o;
|
|
3144
|
+
const char *indent;
|
|
3145
|
+
size_t ilen;
|
|
3146
|
+
size_t depth; /* nesting depth (levels) */
|
|
3147
|
+
size_t gdepth; /* group depth within the current level */
|
|
3148
|
+
int ls; /* at the start of a line */
|
|
3149
|
+
size_t *stack;
|
|
3150
|
+
size_t sn, scap;
|
|
3151
|
+
c0_error *err;
|
|
3152
|
+
} c0__pf;
|
|
3153
|
+
|
|
3154
|
+
static void c0__pf_push(c0__pf *f, size_t v) {
|
|
3155
|
+
if (f->sn == f->scap) {
|
|
3156
|
+
size_t ncap = f->scap ? f->scap * 2 : 16;
|
|
3157
|
+
size_t *ns = (size_t *)realloc(f->stack, ncap * sizeof(size_t));
|
|
3158
|
+
if (!ns) {
|
|
3159
|
+
c0__fail(f->err, C0_ERR_OOM, 0, "out of memory");
|
|
3160
|
+
return;
|
|
3161
|
+
}
|
|
3162
|
+
f->stack = ns;
|
|
3163
|
+
f->scap = ncap;
|
|
3164
|
+
}
|
|
3165
|
+
f->stack[f->sn++] = v;
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
static size_t c0__pf_pop(c0__pf *f) { return f->sn ? f->stack[--f->sn] : 0; }
|
|
3169
|
+
|
|
3170
|
+
/* Pretty form cannot show a value holding a Control Picture (U+2400-U+241F:
|
|
3171
|
+
* UTF-8 E2 90 80-9F). */
|
|
3172
|
+
static void c0__check_glyphs(c0__pf *f, const uint8_t *p, size_t n) {
|
|
3173
|
+
size_t i;
|
|
3174
|
+
for (i = 0; i + 2 < n; i++) {
|
|
3175
|
+
if (p[i] == 0xE2 && p[i + 1] == 0x90 && p[i + 2] >= 0x80 && p[i + 2] <= 0x9F) {
|
|
3176
|
+
c0__fail(f->err, C0_ERR_SYNTAX, 0, "glyph in data");
|
|
3177
|
+
return;
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
static void c0__pf_data(c0__pf *f, c0_bytes d) {
|
|
3183
|
+
c0__check_glyphs(f, d.ptr, d.len);
|
|
3184
|
+
c0__sb_raw(f->o, d.ptr, d.len);
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
/* A whole text (STX … ETX): glyphs for the brackets and for any control
|
|
3188
|
+
* byte inside other than HT, LF and CR; everything else as is. */
|
|
3189
|
+
static void c0__pf_text(c0__pf *f, c0_bytes t) {
|
|
3190
|
+
size_t i;
|
|
3191
|
+
c0__check_glyphs(f, t.ptr, t.len);
|
|
3192
|
+
c0__sb_glyph(f->o, C0_STX);
|
|
3193
|
+
for (i = 1; i + 1 < t.len; i++) {
|
|
3194
|
+
uint8_t b = t.ptr[i];
|
|
3195
|
+
if (b < 0x20 && b != C0_LF && b != C0_CR && b != C0_HT) c0__sb_glyph(f->o, b);
|
|
3196
|
+
else c0__sb_byte(f->o, b);
|
|
3197
|
+
}
|
|
3198
|
+
c0__sb_glyph(f->o, C0_ETX);
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
static size_t c0__pf_plain(c0__pf *f, size_t pos) {
|
|
3202
|
+
size_t start;
|
|
3203
|
+
pos = c0__layout(f->buf, pos, f->len);
|
|
3204
|
+
start = pos;
|
|
3205
|
+
pos = c0__plain(f->buf, pos, f->len);
|
|
3206
|
+
c0__pf_data(f, c0_trim(c0__slice(f->buf, start, pos)));
|
|
3207
|
+
return pos;
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
static void c0__pf_indent(c0__pf *f, size_t depth) {
|
|
3211
|
+
while (depth--) c0__sb_raw(f->o, (const uint8_t *)f->indent, f->ilen);
|
|
3212
|
+
}
|
|
3213
|
+
|
|
3214
|
+
static void c0__pf_nl(c0__pf *f) {
|
|
3215
|
+
if (!f->ls) c0__sb_byte(f->o, '\n');
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
/* A level written on one line (heading values). */
|
|
3219
|
+
static void c0__pf_inline_level(c0__pf *f, c0_bytes raw) {
|
|
3220
|
+
size_t i = 0;
|
|
3221
|
+
while (i < raw.len && !f->err->code) {
|
|
3222
|
+
uint8_t b = raw.ptr[i];
|
|
3223
|
+
if (b == C0_STX) {
|
|
3224
|
+
size_t stop = c0__text(raw.ptr, i, raw.len, f->err);
|
|
3225
|
+
c0__pf_text(f, c0__slice(raw.ptr, i, stop));
|
|
3226
|
+
i = stop;
|
|
3227
|
+
} else if (b >= 0x20) {
|
|
3228
|
+
size_t start = i;
|
|
3229
|
+
i = c0__plain(raw.ptr, i, raw.len);
|
|
3230
|
+
c0__pf_data(f, c0_trim(c0__slice(raw.ptr, start, i)));
|
|
3231
|
+
} else if (c0_is_layout(b)) {
|
|
3232
|
+
i++;
|
|
3233
|
+
} else {
|
|
3234
|
+
c0__sb_glyph(f->o, b);
|
|
3235
|
+
i++;
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
/* Units of a record, header or heading on the current line. Stops at a
|
|
3241
|
+
* record-level code. A level opener writes ␐, breaks the line and raises the
|
|
3242
|
+
* depth; the caller resumes at the matching CAN. */
|
|
3243
|
+
static size_t c0__pf_units(c0__pf *f, size_t pos) {
|
|
3244
|
+
const uint8_t *buf = f->buf;
|
|
3245
|
+
size_t len = f->len;
|
|
3246
|
+
while (pos < len && !f->err->code) {
|
|
3247
|
+
uint8_t b = buf[pos];
|
|
3248
|
+
if (b >= 0x20) {
|
|
3249
|
+
size_t start = pos;
|
|
3250
|
+
pos = c0__plain(buf, pos, len);
|
|
3251
|
+
c0__pf_data(f, c0_trim(c0__slice(buf, start, pos)));
|
|
3252
|
+
} else if (c0_is_layout(b)) {
|
|
3253
|
+
pos++;
|
|
3254
|
+
} else if (b == C0_US || b == C0_ENQ || b == C0_SUB) {
|
|
3255
|
+
c0__sb_glyph(f->o, b);
|
|
3256
|
+
pos++;
|
|
3257
|
+
} else if (b == C0_SOH) {
|
|
3258
|
+
size_t p2 = c0__layout(buf, pos + 1, len);
|
|
3259
|
+
if (p2 < len && buf[p2] == C0_RS) break;
|
|
3260
|
+
c0__sb_glyph(f->o, C0_SOH);
|
|
3261
|
+
pos++;
|
|
3262
|
+
} else if (b == C0_STX) {
|
|
3263
|
+
size_t stop = c0__text(buf, pos, len, f->err);
|
|
3264
|
+
c0__pf_text(f, c0__slice(buf, pos, stop));
|
|
3265
|
+
pos = stop;
|
|
3266
|
+
} else if (b == C0_DLE) {
|
|
3267
|
+
c0__sb_glyph(f->o, C0_DLE);
|
|
3268
|
+
c0__sb_byte(f->o, '\n');
|
|
3269
|
+
c0__pf_push(f, f->gdepth);
|
|
3270
|
+
f->depth++;
|
|
3271
|
+
f->ls = 1;
|
|
3272
|
+
return pos + 1;
|
|
3273
|
+
} else if (b == C0_BEL) {
|
|
3274
|
+
size_t p2 = c0__layout(buf, pos + 1, len);
|
|
3275
|
+
if (p2 < len && (buf[p2] >= 0x20 || buf[p2] == C0_STX || buf[p2] == C0_DLE)) {
|
|
3276
|
+
c0__sb_glyph(f->o, C0_BEL);
|
|
3277
|
+
pos = p2;
|
|
3278
|
+
} else {
|
|
3279
|
+
break;
|
|
3280
|
+
}
|
|
3281
|
+
} else if (b == C0_ETB) {
|
|
3282
|
+
c0__sb_glyph(f->o, C0_ETB);
|
|
3283
|
+
pos = c0__pf_plain(f, pos + 1);
|
|
3284
|
+
} else {
|
|
3285
|
+
break;
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
f->ls = 0;
|
|
3289
|
+
return pos;
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
/* SOH / RS / BEL / ETB at element level: the element and its units on one
|
|
3293
|
+
* line (a level opener breaks the line). */
|
|
3294
|
+
static size_t c0__pf_element(c0__pf *f, size_t pos) {
|
|
3295
|
+
const uint8_t *buf = f->buf;
|
|
3296
|
+
size_t len = f->len;
|
|
3297
|
+
uint8_t b = buf[pos];
|
|
3298
|
+
f->ls = 0;
|
|
3299
|
+
if (b == C0_BEL) {
|
|
3300
|
+
c0__sb_glyph(f->o, C0_BEL);
|
|
3301
|
+
pos = c0__layout(buf, pos + 1, len);
|
|
3302
|
+
if (pos < len && (buf[pos] >= 0x20 || buf[pos] == C0_STX)) {
|
|
3303
|
+
pos = c0__pf_units(f, pos);
|
|
3304
|
+
c0__sb_byte(f->o, '\n');
|
|
3305
|
+
f->ls = 1;
|
|
3306
|
+
return pos;
|
|
3307
|
+
}
|
|
3308
|
+
if (pos >= len) return pos;
|
|
3309
|
+
return c0__pf_element(f, pos); /* a dead element, rendered on this line */
|
|
3310
|
+
} else if (b == C0_SOH) {
|
|
3311
|
+
c0__sb_glyph(f->o, C0_SOH);
|
|
3312
|
+
pos = c0__layout(buf, pos + 1, len);
|
|
3313
|
+
if (pos < len && buf[pos] == C0_RS) {
|
|
3314
|
+
c0__sb_glyph(f->o, C0_RS);
|
|
3315
|
+
pos++;
|
|
3316
|
+
}
|
|
3317
|
+
pos = c0__pf_units(f, pos);
|
|
3318
|
+
} else if (b == C0_RS) {
|
|
3319
|
+
c0__sb_glyph(f->o, C0_RS);
|
|
3320
|
+
pos = c0__pf_units(f, pos + 1);
|
|
3321
|
+
} else if (b == C0_ETB) {
|
|
3322
|
+
c0__sb_glyph(f->o, C0_ETB);
|
|
3323
|
+
pos = c0__pf_plain(f, pos + 1);
|
|
3324
|
+
}
|
|
3325
|
+
if (!f->ls) {
|
|
3326
|
+
c0__sb_byte(f->o, '\n');
|
|
3327
|
+
f->ls = 1;
|
|
3328
|
+
}
|
|
3329
|
+
return pos;
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
static size_t c0__pf_heading(c0__pf *f, size_t pos) {
|
|
3333
|
+
const uint8_t *buf = f->buf;
|
|
3334
|
+
size_t len = f->len;
|
|
3335
|
+
while (pos < len && !f->err->code) {
|
|
3336
|
+
uint8_t b = buf[pos];
|
|
3337
|
+
if (b == C0_SOH) {
|
|
3338
|
+
size_t p2 = c0__layout(buf, pos + 1, len);
|
|
3339
|
+
if (p2 < len && buf[p2] == C0_RS) break;
|
|
3340
|
+
c0__sb_glyph(f->o, C0_SOH);
|
|
3341
|
+
pos = c0__pf_plain(f, p2);
|
|
3342
|
+
if (pos < len && buf[pos] == C0_US) {
|
|
3343
|
+
c0__sb_glyph(f->o, C0_US);
|
|
3344
|
+
pos = c0__layout(buf, pos + 1, len);
|
|
3345
|
+
if (pos < len && buf[pos] == C0_STX) {
|
|
3346
|
+
size_t stop = c0__text(buf, pos, len, f->err);
|
|
3347
|
+
c0__pf_text(f, c0__slice(buf, pos, stop));
|
|
3348
|
+
pos = stop;
|
|
3349
|
+
} else if (pos < len && buf[pos] == C0_DLE) {
|
|
3350
|
+
size_t stop = c0__level(buf, pos, len, f->err);
|
|
3351
|
+
c0__pf_inline_level(f, c0__slice(buf, pos, stop));
|
|
3352
|
+
pos = stop;
|
|
3353
|
+
} else {
|
|
3354
|
+
pos = c0__pf_plain(f, pos);
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
} else if (b == C0_BEL) {
|
|
3358
|
+
size_t p2 = c0__layout(buf, pos + 1, len);
|
|
3359
|
+
if (p2 < len && (buf[p2] >= 0x20 || buf[p2] == C0_STX)) {
|
|
3360
|
+
c0__sb_glyph(f->o, C0_BEL);
|
|
3361
|
+
pos = p2;
|
|
3362
|
+
if (buf[pos] == C0_STX) {
|
|
3363
|
+
size_t stop = c0__text(buf, pos, len, f->err);
|
|
3364
|
+
c0__pf_text(f, c0__slice(buf, pos, stop));
|
|
3365
|
+
pos = stop;
|
|
3366
|
+
} else {
|
|
3367
|
+
pos = c0__pf_plain(f, pos);
|
|
3368
|
+
}
|
|
3369
|
+
} else if (p2 < len && buf[p2] == C0_SOH) {
|
|
3370
|
+
c0__sb_glyph(f->o, C0_BEL);
|
|
3371
|
+
pos = p2;
|
|
3372
|
+
} else {
|
|
3373
|
+
break;
|
|
3374
|
+
}
|
|
3375
|
+
} else if (c0_is_layout(b)) {
|
|
3376
|
+
pos++;
|
|
3377
|
+
} else {
|
|
3378
|
+
break;
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
return pos;
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3384
|
+
static void c0__pf_compact(c0__pf *f) {
|
|
3385
|
+
const uint8_t *buf = f->buf;
|
|
3386
|
+
size_t len = f->len, pos = 0;
|
|
3387
|
+
f->depth = 0;
|
|
3388
|
+
f->gdepth = 0;
|
|
3389
|
+
f->ls = 1;
|
|
3390
|
+
while (pos < len && !f->err->code) {
|
|
3391
|
+
uint8_t b = buf[pos];
|
|
3392
|
+
if (b >= 0x20 || c0_is_layout(b)) {
|
|
3393
|
+
if (c0_is_layout(b)) {
|
|
3394
|
+
pos++;
|
|
3395
|
+
} else {
|
|
3396
|
+
size_t start = pos;
|
|
3397
|
+
pos = c0__plain(buf, pos, len);
|
|
3398
|
+
c0__pf_data(f, c0__slice(buf, start, pos));
|
|
3399
|
+
f->ls = 0;
|
|
3400
|
+
}
|
|
3401
|
+
continue;
|
|
3402
|
+
}
|
|
3403
|
+
switch (b) {
|
|
3404
|
+
case C0_SYN:
|
|
3405
|
+
c0__pf_nl(f);
|
|
3406
|
+
c0__sb_glyph(f->o, C0_SYN);
|
|
3407
|
+
pos = c0__pf_heading(f, pos + 1);
|
|
3408
|
+
c0__sb_byte(f->o, '\n');
|
|
3409
|
+
f->ls = 1;
|
|
3410
|
+
f->gdepth = 0;
|
|
3411
|
+
break;
|
|
3412
|
+
case C0_FS:
|
|
3413
|
+
c0__pf_nl(f);
|
|
3414
|
+
c0__pf_indent(f, f->depth);
|
|
3415
|
+
c0__sb_glyph(f->o, C0_FS);
|
|
3416
|
+
pos = c0__pf_plain(f, pos + 1);
|
|
3417
|
+
c0__sb_byte(f->o, '\n');
|
|
3418
|
+
f->ls = 1;
|
|
3419
|
+
f->gdepth = 1;
|
|
3420
|
+
break;
|
|
3421
|
+
case C0_GS: {
|
|
3422
|
+
size_t run = 0;
|
|
3423
|
+
while (pos < len && buf[pos] == C0_GS) {
|
|
3424
|
+
run++;
|
|
3425
|
+
pos++;
|
|
3426
|
+
}
|
|
3427
|
+
c0__pf_nl(f);
|
|
3428
|
+
c0__pf_indent(f, f->depth);
|
|
3429
|
+
while (run--) c0__sb_glyph(f->o, C0_GS);
|
|
3430
|
+
pos = c0__pf_plain(f, pos);
|
|
3431
|
+
c0__sb_byte(f->o, '\n');
|
|
3432
|
+
f->ls = 1;
|
|
3433
|
+
f->gdepth = 1;
|
|
3434
|
+
break;
|
|
3435
|
+
}
|
|
3436
|
+
case C0_SOH:
|
|
3437
|
+
case C0_RS:
|
|
3438
|
+
case C0_BEL:
|
|
3439
|
+
case C0_ETB:
|
|
3440
|
+
c0__pf_nl(f);
|
|
3441
|
+
c0__pf_indent(f, f->depth + f->gdepth);
|
|
3442
|
+
pos = c0__pf_element(f, pos);
|
|
3443
|
+
break;
|
|
3444
|
+
case C0_CAN:
|
|
3445
|
+
if (f->depth > 0) f->depth--;
|
|
3446
|
+
f->gdepth = c0__pf_pop(f);
|
|
3447
|
+
c0__pf_nl(f);
|
|
3448
|
+
c0__pf_indent(f, f->depth + f->gdepth);
|
|
3449
|
+
c0__sb_glyph(f->o, C0_CAN);
|
|
3450
|
+
pos++;
|
|
3451
|
+
f->ls = 0;
|
|
3452
|
+
pos = c0__pf_units(f, pos); /* the rest of the enclosing record */
|
|
3453
|
+
if (!f->ls) {
|
|
3454
|
+
c0__sb_byte(f->o, '\n');
|
|
3455
|
+
f->ls = 1;
|
|
3456
|
+
}
|
|
3457
|
+
break;
|
|
3458
|
+
case C0_EOT:
|
|
3459
|
+
c0__pf_nl(f);
|
|
3460
|
+
c0__sb_glyph(f->o, C0_EOT);
|
|
3461
|
+
c0__sb_byte(f->o, '\n');
|
|
3462
|
+
pos++;
|
|
3463
|
+
f->ls = 1;
|
|
3464
|
+
break;
|
|
3465
|
+
case C0_EM: {
|
|
3466
|
+
size_t tail = len - pos - 1;
|
|
3467
|
+
c0__pf_nl(f);
|
|
3468
|
+
c0__sb_glyph(f->o, C0_EM);
|
|
3469
|
+
if (tail > 0) {
|
|
3470
|
+
char num[32];
|
|
3471
|
+
size_t k = sizeof(num);
|
|
3472
|
+
num[--k] = 0;
|
|
3473
|
+
do {
|
|
3474
|
+
num[--k] = (char)('0' + tail % 10);
|
|
3475
|
+
tail /= 10;
|
|
3476
|
+
} while (tail);
|
|
3477
|
+
c0__sb_byte(f->o, ' ');
|
|
3478
|
+
c0__sb_cstr(f->o, num + k);
|
|
3479
|
+
c0__sb_cstr(f->o, " bytes");
|
|
3480
|
+
}
|
|
3481
|
+
c0__sb_byte(f->o, '\n');
|
|
3482
|
+
return;
|
|
3483
|
+
}
|
|
3484
|
+
case C0_STX:
|
|
3485
|
+
case C0_DLE:
|
|
3486
|
+
case C0_ENQ:
|
|
3487
|
+
case C0_US:
|
|
3488
|
+
case C0_SUB:
|
|
3489
|
+
/* a unit-level code at element level (e.g. a diff pattern): inline */
|
|
3490
|
+
pos = c0__pf_units(f, pos);
|
|
3491
|
+
c0__sb_byte(f->o, '\n');
|
|
3492
|
+
f->ls = 1;
|
|
3493
|
+
break;
|
|
3494
|
+
default:
|
|
3495
|
+
c0__fail(f->err, C0_ERR_UNASSIGNED, pos, "unassigned control code");
|
|
3496
|
+
return;
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
if (!f->ls) c0__sb_byte(f->o, '\n');
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3502
|
+
/* ---- alignment ---- */
|
|
3503
|
+
|
|
3504
|
+
static int c0__glyph_at(c0_bytes s, size_t i, uint8_t *code) {
|
|
3505
|
+
if (i + 2 < s.len && s.ptr[i] == 0xE2 && s.ptr[i + 1] == 0x90 && (s.ptr[i + 2] & 0xE0) == 0x80) {
|
|
3506
|
+
*code = (uint8_t)(s.ptr[i + 2] & 0x1F);
|
|
3507
|
+
return 1;
|
|
3508
|
+
}
|
|
3509
|
+
return 0;
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3512
|
+
static int c0__starts_glyph(c0_bytes s, uint8_t code) {
|
|
3513
|
+
uint8_t c;
|
|
3514
|
+
return c0__glyph_at(s, 0, &c) && c == code;
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
static size_t c0__cp_count(c0_bytes s) {
|
|
3518
|
+
size_t i, n = 0;
|
|
3519
|
+
for (i = 0; i < s.len; i++) {
|
|
3520
|
+
if ((s.ptr[i] & 0xC0) != 0x80) n++;
|
|
3521
|
+
}
|
|
3522
|
+
return n;
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
static size_t c0__utf8_adv(c0_bytes s, size_t i) {
|
|
3526
|
+
uint8_t c = s.ptr[i];
|
|
3527
|
+
size_t adv = 1;
|
|
3528
|
+
if ((c >> 5) == 0x6) adv = 2;
|
|
3529
|
+
else if ((c >> 4) == 0xE) adv = 3;
|
|
3530
|
+
else if ((c >> 3) == 0x1E) adv = 4;
|
|
3531
|
+
if (i + adv > s.len) adv = s.len - i;
|
|
3532
|
+
return adv;
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
typedef struct {
|
|
3536
|
+
size_t line;
|
|
3537
|
+
c0_bytes prefix;
|
|
3538
|
+
c0_bytes *fields;
|
|
3539
|
+
int *sep_us; /* sep_us[i]: a ␟ stood between fields i and i+1 (else an SOH mark begins i+1) */
|
|
3540
|
+
size_t n, cap;
|
|
3541
|
+
} c0__tline;
|
|
3542
|
+
|
|
3543
|
+
static int c0__tl_push(c0__tline *t, c0_bytes field, int sep) {
|
|
3544
|
+
if (t->n == t->cap) {
|
|
3545
|
+
size_t ncap = t->cap ? t->cap * 2 : 8;
|
|
3546
|
+
c0_bytes *nf = (c0_bytes *)realloc(t->fields, ncap * sizeof(c0_bytes));
|
|
3547
|
+
int *ns;
|
|
3548
|
+
if (!nf) return 0;
|
|
3549
|
+
t->fields = nf;
|
|
3550
|
+
ns = (int *)realloc(t->sep_us, ncap * sizeof(int));
|
|
3551
|
+
if (!ns) return 0;
|
|
3552
|
+
t->sep_us = ns;
|
|
3553
|
+
t->cap = ncap;
|
|
3554
|
+
}
|
|
3555
|
+
t->fields[t->n] = c0_trim(field);
|
|
3556
|
+
t->sep_us[t->n] = sep;
|
|
3557
|
+
t->n++;
|
|
3558
|
+
return 1;
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
/* Split a ␞ or ␁␞ line into its units on ␟ (text is not split; a keyed
|
|
3562
|
+
* record splits on its ␁ marks too). Returns 1 and fills *t, 0 when the
|
|
3563
|
+
* line is not a table line, -1 on OOM. */
|
|
3564
|
+
static int c0__parse_table_line(c0_bytes text, size_t line, c0__tline *t) {
|
|
3565
|
+
size_t i = 0, field_start;
|
|
3566
|
+
uint8_t code;
|
|
3567
|
+
int in_text = 0, keyed;
|
|
3568
|
+
memset(t, 0, sizeof(*t));
|
|
3569
|
+
t->line = line;
|
|
3570
|
+
while (i < text.len && (text.ptr[i] == ' ' || text.ptr[i] == '\t')) i++;
|
|
3571
|
+
if (i >= text.len) return 0;
|
|
3572
|
+
if (c0__glyph_at(text, i, &code) && code == C0_SOH) {
|
|
3573
|
+
i += 3;
|
|
3574
|
+
if (!(c0__glyph_at(text, i, &code) && code == C0_RS)) return 0;
|
|
3575
|
+
i += 3;
|
|
3576
|
+
} else if (c0__glyph_at(text, i, &code) && code == C0_RS) {
|
|
3577
|
+
i += 3;
|
|
3578
|
+
} else {
|
|
3579
|
+
return 0;
|
|
3580
|
+
}
|
|
3581
|
+
t->prefix = c0__slice(text.ptr, 0, i);
|
|
3582
|
+
keyed = c0__glyph_at(text, i, &code) && code == C0_SOH;
|
|
3583
|
+
field_start = i;
|
|
3584
|
+
while (i < text.len) {
|
|
3585
|
+
size_t adv = c0__utf8_adv(text, i);
|
|
3586
|
+
int is_glyph = c0__glyph_at(text, i, &code);
|
|
3587
|
+
if (in_text) {
|
|
3588
|
+
if (is_glyph && code == C0_ETX) in_text = 0;
|
|
3589
|
+
i += adv;
|
|
3590
|
+
if (is_glyph && code == C0_DLE && i < text.len) i += c0__utf8_adv(text, i);
|
|
3591
|
+
} else if (is_glyph && code == C0_STX) {
|
|
3592
|
+
in_text = 1;
|
|
3593
|
+
i += adv;
|
|
3594
|
+
} else if (is_glyph && (code == C0_DLE || code == C0_CAN)) {
|
|
3595
|
+
goto not_table; /* nested levels are not aligned */
|
|
3596
|
+
} else if (is_glyph && code == C0_US) {
|
|
3597
|
+
if (!c0__tl_push(t, c0__slice(text.ptr, field_start, i), 1)) goto oom;
|
|
3598
|
+
i += adv;
|
|
3599
|
+
field_start = i;
|
|
3600
|
+
} else if (keyed && is_glyph && code == C0_SOH && i > t->prefix.len) {
|
|
3601
|
+
if (!c0__tl_push(t, c0__slice(text.ptr, field_start, i), 0)) goto oom;
|
|
3602
|
+
field_start = i;
|
|
3603
|
+
i += adv;
|
|
3604
|
+
} else {
|
|
3605
|
+
i += adv;
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
if (in_text) goto not_table; /* text continues on the next line */
|
|
3609
|
+
if (!c0__tl_push(t, c0__slice(text.ptr, field_start, text.len), 1)) goto oom;
|
|
3610
|
+
if (t->n < 2) goto not_table;
|
|
3611
|
+
return 1;
|
|
3612
|
+
not_table:
|
|
3613
|
+
free(t->fields);
|
|
3614
|
+
free(t->sep_us);
|
|
3615
|
+
memset(t, 0, sizeof(*t));
|
|
3616
|
+
return 0;
|
|
3617
|
+
oom:
|
|
3618
|
+
free(t->fields);
|
|
3619
|
+
free(t->sep_us);
|
|
3620
|
+
memset(t, 0, sizeof(*t));
|
|
3621
|
+
return -1;
|
|
3622
|
+
}
|
|
3623
|
+
|
|
3624
|
+
static int c0__is_prefix_glyph(uint8_t c) {
|
|
3625
|
+
return c == C0_FS || c == C0_GS || c == C0_RS || c == C0_SOH || c == C0_US || c == C0_SYN || c == C0_BEL;
|
|
3626
|
+
}
|
|
3627
|
+
|
|
3628
|
+
/* Reformat a compact pretty string with column alignment. Keyed records
|
|
3629
|
+
* align on their US like two-column tables. Returns 0 on OOM. */
|
|
3630
|
+
static int c0__align(const uint8_t *pretty, size_t plen, int spaced, c0__sb *out) {
|
|
3631
|
+
c0_bytes *lines = NULL;
|
|
3632
|
+
size_t nlines = 0, i, s = 0;
|
|
3633
|
+
c0__tline *tl = NULL; /* table lines, grouped contiguously */
|
|
3634
|
+
size_t ntl = 0, tlcap = 0;
|
|
3635
|
+
size_t *gstart = NULL; /* start index into tl of each group */
|
|
3636
|
+
size_t ng = 0, gcap = 0;
|
|
3637
|
+
int in_group = 0;
|
|
3638
|
+
c0__sb *repl = NULL; /* replacement text per line (NULL = unchanged) */
|
|
3639
|
+
int ok = 0;
|
|
3640
|
+
const char *sp = spaced ? " " : "";
|
|
3641
|
+
|
|
3642
|
+
/* split lines */
|
|
3643
|
+
for (i = 0; i <= plen; i++) {
|
|
3644
|
+
if (i == plen || pretty[i] == '\n') {
|
|
3645
|
+
if (i == plen && i == s) break; /* drop the empty last line */
|
|
3646
|
+
{
|
|
3647
|
+
c0_bytes *nl = (c0_bytes *)realloc(lines, (nlines + 1) * sizeof(c0_bytes));
|
|
3648
|
+
if (!nl) goto done;
|
|
3649
|
+
lines = nl;
|
|
3650
|
+
}
|
|
3651
|
+
lines[nlines++] = c0__slice(pretty, s, i);
|
|
3652
|
+
s = i + 1;
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
repl = (c0__sb *)calloc(nlines ? nlines : 1, sizeof(c0__sb));
|
|
3656
|
+
if (!repl) goto done;
|
|
3657
|
+
|
|
3658
|
+
/* find table groups */
|
|
3659
|
+
for (i = 0; i < nlines; i++) {
|
|
3660
|
+
c0_bytes trimmed = c0_trim(lines[i]);
|
|
3661
|
+
int close = 0;
|
|
3662
|
+
if (trimmed.len == 0 || c0__starts_glyph(trimmed, C0_GS) || c0__starts_glyph(trimmed, C0_FS) ||
|
|
3663
|
+
c0__starts_glyph(trimmed, C0_EOT) || c0__starts_glyph(trimmed, C0_SYN) || c0__starts_glyph(trimmed, C0_CAN)) {
|
|
3664
|
+
close = 1;
|
|
3665
|
+
} else if (c0__starts_glyph(trimmed, C0_SOH) || c0__starts_glyph(trimmed, C0_RS)) {
|
|
3666
|
+
c0__tline t;
|
|
3667
|
+
int r = c0__parse_table_line(lines[i], i, &t);
|
|
3668
|
+
if (r < 0) goto done;
|
|
3669
|
+
if (r == 0) {
|
|
3670
|
+
close = 1;
|
|
3671
|
+
} else {
|
|
3672
|
+
if (ntl == tlcap) {
|
|
3673
|
+
size_t ncap = tlcap ? tlcap * 2 : 16;
|
|
3674
|
+
c0__tline *nt = (c0__tline *)realloc(tl, ncap * sizeof(c0__tline));
|
|
3675
|
+
if (!nt) {
|
|
3676
|
+
free(t.fields);
|
|
3677
|
+
free(t.sep_us);
|
|
3678
|
+
goto done;
|
|
3679
|
+
}
|
|
3680
|
+
tl = nt;
|
|
3681
|
+
tlcap = ncap;
|
|
3682
|
+
}
|
|
3683
|
+
if (!in_group) {
|
|
3684
|
+
if (ng == gcap) {
|
|
3685
|
+
size_t ncap = gcap ? gcap * 2 : 16;
|
|
3686
|
+
size_t *ngs = (size_t *)realloc(gstart, ncap * sizeof(size_t));
|
|
3687
|
+
if (!ngs) {
|
|
3688
|
+
free(t.fields);
|
|
3689
|
+
free(t.sep_us);
|
|
3690
|
+
goto done;
|
|
3691
|
+
}
|
|
3692
|
+
gstart = ngs;
|
|
3693
|
+
gcap = ncap;
|
|
3694
|
+
}
|
|
3695
|
+
gstart[ng++] = ntl;
|
|
3696
|
+
in_group = 1;
|
|
3697
|
+
}
|
|
3698
|
+
tl[ntl++] = t;
|
|
3699
|
+
}
|
|
3700
|
+
} else {
|
|
3701
|
+
close = 1;
|
|
3702
|
+
}
|
|
3703
|
+
if (close) in_group = 0;
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
/* align each group */
|
|
3707
|
+
for (i = 0; i < ng; i++) {
|
|
3708
|
+
size_t from = gstart[i], to = (i + 1 < ng) ? gstart[i + 1] : ntl, j, c, cols = 0;
|
|
3709
|
+
size_t *widths;
|
|
3710
|
+
for (j = from; j < to; j++) {
|
|
3711
|
+
if (tl[j].n > cols) cols = tl[j].n;
|
|
3712
|
+
}
|
|
3713
|
+
widths = (size_t *)calloc(cols ? cols : 1, sizeof(size_t));
|
|
3714
|
+
if (!widths) goto done;
|
|
3715
|
+
for (j = from; j < to; j++) {
|
|
3716
|
+
for (c = 0; c < tl[j].n; c++) {
|
|
3717
|
+
size_t w = c0__cp_count(tl[j].fields[c]);
|
|
3718
|
+
if (w > widths[c]) widths[c] = w;
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
for (j = from; j < to; j++) {
|
|
3722
|
+
c0__sb *o = &repl[tl[j].line];
|
|
3723
|
+
c0__sb_raw(o, tl[j].prefix.ptr, tl[j].prefix.len);
|
|
3724
|
+
c0__sb_cstr(o, sp);
|
|
3725
|
+
for (c = 0; c < tl[j].n; c++) {
|
|
3726
|
+
c0__sb_raw(o, tl[j].fields[c].ptr, tl[j].fields[c].len);
|
|
3727
|
+
if (c + 1 < tl[j].n) {
|
|
3728
|
+
c0__sb_spaces(o, widths[c] - c0__cp_count(tl[j].fields[c]));
|
|
3729
|
+
if (tl[j].sep_us[c]) {
|
|
3730
|
+
c0__sb_cstr(o, sp);
|
|
3731
|
+
c0__sb_glyph(o, C0_US);
|
|
3732
|
+
c0__sb_cstr(o, sp);
|
|
3733
|
+
} else {
|
|
3734
|
+
c0__sb_byte(o, ' ');
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
if (o->oom) {
|
|
3739
|
+
free(widths);
|
|
3740
|
+
goto done;
|
|
3741
|
+
}
|
|
3742
|
+
}
|
|
3743
|
+
free(widths);
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3746
|
+
/* other lines: one space after the prefix glyphs in spaced mode */
|
|
3747
|
+
for (i = 0; i < nlines; i++) {
|
|
3748
|
+
c0_bytes text = lines[i];
|
|
3749
|
+
size_t ws_end = 0, glyph_end;
|
|
3750
|
+
uint8_t code;
|
|
3751
|
+
c0_bytes rest;
|
|
3752
|
+
c0__sb *o = &repl[i];
|
|
3753
|
+
if (o->d) continue;
|
|
3754
|
+
if (c0_trim(text).len == 0) continue;
|
|
3755
|
+
while (ws_end < text.len && (text.ptr[ws_end] == ' ' || text.ptr[ws_end] == '\t')) ws_end++;
|
|
3756
|
+
glyph_end = ws_end;
|
|
3757
|
+
while (c0__glyph_at(text, glyph_end, &code) && c0__is_prefix_glyph(code)) glyph_end += 3;
|
|
3758
|
+
if (glyph_end == ws_end || glyph_end >= text.len) continue;
|
|
3759
|
+
rest = c0__slice(text.ptr, glyph_end, text.len);
|
|
3760
|
+
while (rest.len && c0_is_layout(rest.ptr[0])) {
|
|
3761
|
+
rest.ptr++;
|
|
3762
|
+
rest.len--;
|
|
3763
|
+
}
|
|
3764
|
+
c0__sb_raw(o, text.ptr, glyph_end);
|
|
3765
|
+
c0__sb_cstr(o, sp);
|
|
3766
|
+
c0__sb_raw(o, rest.ptr, rest.len);
|
|
3767
|
+
if (o->oom) goto done;
|
|
3768
|
+
}
|
|
3769
|
+
|
|
3770
|
+
for (i = 0; i < nlines; i++) {
|
|
3771
|
+
if (repl[i].d) c0__sb_raw(out, repl[i].d, repl[i].n);
|
|
3772
|
+
else c0__sb_raw(out, lines[i].ptr, lines[i].len);
|
|
3773
|
+
c0__sb_byte(out, '\n');
|
|
3774
|
+
}
|
|
3775
|
+
ok = !out->oom;
|
|
3776
|
+
done:
|
|
3777
|
+
for (i = 0; i < ntl; i++) {
|
|
3778
|
+
free(tl[i].fields);
|
|
3779
|
+
free(tl[i].sep_us);
|
|
3780
|
+
}
|
|
3781
|
+
free(tl);
|
|
3782
|
+
free(gstart);
|
|
3783
|
+
if (repl) {
|
|
3784
|
+
for (i = 0; i < nlines; i++) free(repl[i].d);
|
|
3785
|
+
free(repl);
|
|
3786
|
+
}
|
|
3787
|
+
free(lines);
|
|
3788
|
+
return ok;
|
|
3789
|
+
}
|
|
3790
|
+
|
|
3791
|
+
char *c0_pretty_format(const uint8_t *buf, size_t len, const char *indent, c0_pretty_mode mode, size_t *outlen, c0_error *err) {
|
|
3792
|
+
c0__sb o = {NULL, 0, 0, 0}, aligned = {NULL, 0, 0, 0};
|
|
3793
|
+
c0__pf f;
|
|
3794
|
+
c0_error local = {C0_OK, 0, ""};
|
|
3795
|
+
if (!err) err = &local;
|
|
3796
|
+
c0__err_clear(err);
|
|
3797
|
+
memset(&f, 0, sizeof(f));
|
|
3798
|
+
f.buf = buf;
|
|
3799
|
+
f.len = len;
|
|
3800
|
+
f.o = &o;
|
|
3801
|
+
f.indent = indent ? indent : " ";
|
|
3802
|
+
f.ilen = strlen(f.indent);
|
|
3803
|
+
f.err = err;
|
|
3804
|
+
c0__pf_compact(&f);
|
|
3805
|
+
free(f.stack);
|
|
3806
|
+
if (o.oom) c0__fail(err, C0_ERR_OOM, 0, "out of memory");
|
|
3807
|
+
if (err->code) {
|
|
3808
|
+
free(o.d);
|
|
3809
|
+
return NULL;
|
|
3810
|
+
}
|
|
3811
|
+
if (mode != C0_PRETTY_COMPACT) {
|
|
3812
|
+
if (!c0__align(o.d, o.n, mode == C0_PRETTY_SPACED, &aligned)) {
|
|
3813
|
+
free(o.d);
|
|
3814
|
+
free(aligned.d);
|
|
3815
|
+
c0__fail(err, C0_ERR_OOM, 0, "out of memory");
|
|
3816
|
+
return NULL;
|
|
3817
|
+
}
|
|
3818
|
+
free(o.d);
|
|
3819
|
+
o = aligned;
|
|
3820
|
+
}
|
|
3821
|
+
c0__sb_byte(&o, 0);
|
|
3822
|
+
if (o.oom) {
|
|
3823
|
+
free(o.d);
|
|
3824
|
+
c0__fail(err, C0_ERR_OOM, 0, "out of memory");
|
|
3825
|
+
return NULL;
|
|
3826
|
+
}
|
|
3827
|
+
if (outlen) *outlen = o.n - 1;
|
|
3828
|
+
return (char *)o.d;
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3831
|
+
/* Decode one UTF-8 code point at s[i]; *adv gets its byte length. */
|
|
3832
|
+
static unsigned c0__utf8(const uint8_t *s, size_t len, size_t i, size_t *adv) {
|
|
3833
|
+
uint8_t c = s[i];
|
|
3834
|
+
if (c < 0x80) {
|
|
3835
|
+
*adv = 1;
|
|
3836
|
+
return c;
|
|
3837
|
+
} else if ((c >> 5) == 0x6 && i + 1 < len) {
|
|
3838
|
+
*adv = 2;
|
|
3839
|
+
return (unsigned)((c & 0x1F) << 6) | (s[i + 1] & 0x3F);
|
|
3840
|
+
} else if ((c >> 4) == 0xE && i + 2 < len) {
|
|
3841
|
+
*adv = 3;
|
|
3842
|
+
return (unsigned)((c & 0x0F) << 12) | ((unsigned)(s[i + 1] & 0x3F) << 6) | (s[i + 2] & 0x3F);
|
|
3843
|
+
} else if ((c >> 3) == 0x1E && i + 3 < len) {
|
|
3844
|
+
*adv = 4;
|
|
3845
|
+
return (unsigned)((c & 0x07) << 18) | ((unsigned)(s[i + 1] & 0x3F) << 12) |
|
|
3846
|
+
((unsigned)(s[i + 2] & 0x3F) << 6) | (s[i + 3] & 0x3F);
|
|
3847
|
+
}
|
|
3848
|
+
*adv = 1;
|
|
3849
|
+
return c;
|
|
3850
|
+
}
|
|
3851
|
+
|
|
3852
|
+
/* Inside text: everything verbatim until the ETX glyph; a DLE glyph takes
|
|
3853
|
+
* the next glyph with it. */
|
|
3854
|
+
static size_t c0__pp_text(const uint8_t *str, size_t len, size_t i, c0__sb *o) {
|
|
3855
|
+
while (i < len) {
|
|
3856
|
+
size_t adv;
|
|
3857
|
+
unsigned cp = c0__utf8(str, len, i, &adv);
|
|
3858
|
+
if (cp >= 0x2400 && cp <= 0x241F) {
|
|
3859
|
+
uint8_t code = (uint8_t)(cp - 0x2400);
|
|
3860
|
+
c0__sb_byte(o, code);
|
|
3861
|
+
i += adv;
|
|
3862
|
+
if (code == C0_DLE && i < len) {
|
|
3863
|
+
unsigned nx = c0__utf8(str, len, i, &adv);
|
|
3864
|
+
if (nx >= 0x2400 && nx <= 0x241F) {
|
|
3865
|
+
c0__sb_byte(o, (uint8_t)(nx - 0x2400));
|
|
3866
|
+
i += adv;
|
|
3867
|
+
}
|
|
3868
|
+
} else if (code == C0_ETX) {
|
|
3869
|
+
return i;
|
|
3870
|
+
}
|
|
3871
|
+
} else {
|
|
3872
|
+
c0__sb_raw(o, str + i, adv);
|
|
3873
|
+
i += adv;
|
|
3874
|
+
}
|
|
3875
|
+
}
|
|
3876
|
+
return i;
|
|
3877
|
+
}
|
|
3878
|
+
|
|
3879
|
+
uint8_t *c0_pretty_parse(const char *str_, size_t len, size_t *outlen) {
|
|
3880
|
+
const uint8_t *str = (const uint8_t *)str_;
|
|
3881
|
+
c0__sb o = {NULL, 0, 0, 0}, pending = {NULL, 0, 0, 0};
|
|
3882
|
+
size_t i = 0;
|
|
3883
|
+
int after_code = 1;
|
|
3884
|
+
while (i < len) {
|
|
3885
|
+
size_t adv;
|
|
3886
|
+
unsigned cp = c0__utf8(str, len, i, &adv);
|
|
3887
|
+
if (cp >= 0x2400 && cp <= 0x241F) {
|
|
3888
|
+
uint8_t code = (uint8_t)(cp - 0x2400);
|
|
3889
|
+
pending.n = 0;
|
|
3890
|
+
c0__sb_byte(&o, code);
|
|
3891
|
+
i += adv;
|
|
3892
|
+
if (code == C0_STX) {
|
|
3893
|
+
i = c0__pp_text(str, len, i, &o);
|
|
3894
|
+
} else if (code == C0_EM) {
|
|
3895
|
+
while (i < len && str[i] != '\n') i++; /* the rest of the line is a summary */
|
|
3896
|
+
}
|
|
3897
|
+
after_code = 1;
|
|
3898
|
+
} else if (cp == '\n' || cp == '\r') {
|
|
3899
|
+
pending.n = 0;
|
|
3900
|
+
after_code = 1;
|
|
3901
|
+
i += adv;
|
|
3902
|
+
} else if (cp == ' ' || cp == '\t') {
|
|
3903
|
+
if (!after_code) c0__sb_byte(&pending, (uint8_t)cp);
|
|
3904
|
+
i += adv;
|
|
3905
|
+
} else {
|
|
3906
|
+
if (pending.n) {
|
|
3907
|
+
c0__sb_raw(&o, pending.d, pending.n);
|
|
3908
|
+
pending.n = 0;
|
|
3909
|
+
}
|
|
3910
|
+
after_code = 0;
|
|
3911
|
+
c0__sb_raw(&o, str + i, adv);
|
|
3912
|
+
i += adv;
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
free(pending.d);
|
|
3916
|
+
if (o.oom || pending.oom) {
|
|
3917
|
+
free(o.d);
|
|
3918
|
+
return NULL;
|
|
3919
|
+
}
|
|
3920
|
+
if (!o.d) o.d = (uint8_t *)malloc(1); /* non-NULL even when empty */
|
|
3921
|
+
if (outlen) *outlen = o.n;
|
|
3922
|
+
return o.d;
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
#endif /* C0_IMPLEMENTATION */
|
|
3926
|
+
#endif /* C0_H */
|