627 lines
15 KiB
C
627 lines
15 KiB
C
|
|
#include "amduat/fps.h"
|
||
|
|
|
||
|
|
#include <stdbool.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
const uint8_t *data;
|
||
|
|
size_t len;
|
||
|
|
size_t offset;
|
||
|
|
} cursor_t;
|
||
|
|
|
||
|
|
static bool read_file(const char *path, uint8_t **out_bytes, size_t *out_len) {
|
||
|
|
FILE *fp;
|
||
|
|
long size;
|
||
|
|
uint8_t *buffer;
|
||
|
|
size_t read_len;
|
||
|
|
|
||
|
|
if (path == NULL || out_bytes == NULL || out_len == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
*out_bytes = NULL;
|
||
|
|
*out_len = 0u;
|
||
|
|
|
||
|
|
fp = fopen(path, "rb");
|
||
|
|
if (fp == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (fseek(fp, 0, SEEK_END) != 0) {
|
||
|
|
fclose(fp);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
size = ftell(fp);
|
||
|
|
if (size < 0) {
|
||
|
|
fclose(fp);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (fseek(fp, 0, SEEK_SET) != 0) {
|
||
|
|
fclose(fp);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
buffer = (uint8_t *)malloc((size_t)size + 1u);
|
||
|
|
if (buffer == NULL) {
|
||
|
|
fclose(fp);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
read_len = fread(buffer, 1u, (size_t)size, fp);
|
||
|
|
fclose(fp);
|
||
|
|
if (read_len != (size_t)size) {
|
||
|
|
free(buffer);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
buffer[size] = '\0';
|
||
|
|
|
||
|
|
*out_bytes = buffer;
|
||
|
|
*out_len = (size_t)size;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool build_fixture_path(char *out, size_t out_len) {
|
||
|
|
const char *needle = "/tests/enc/";
|
||
|
|
const char *path = __FILE__;
|
||
|
|
const char *pos = strstr(path, needle);
|
||
|
|
size_t prefix_len;
|
||
|
|
|
||
|
|
if (pos == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
prefix_len = (size_t)(pos - path);
|
||
|
|
if (prefix_len + strlen("/tests/fixtures/conformance/"
|
||
|
|
"fps_1/manifest.json") + 1u >
|
||
|
|
out_len) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
memcpy(out, path, prefix_len);
|
||
|
|
out[prefix_len] = '\0';
|
||
|
|
strcat(out, "/tests/fixtures/conformance/fps_1/manifest.json");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool cursor_expect(cursor_t *cur, const char *literal) {
|
||
|
|
size_t len = strlen(literal);
|
||
|
|
if (cur->len - cur->offset < len) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (memcmp(cur->data + cur->offset, literal, len) != 0) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
cur->offset += len;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool cursor_skip_ws(cursor_t *cur) {
|
||
|
|
while (cur->offset < cur->len) {
|
||
|
|
char c = (char)cur->data[cur->offset];
|
||
|
|
if (c != ' ' && c != '\n' && c != '\r' && c != '\t') {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
cur->offset++;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool cursor_read_string(cursor_t *cur, char **out) {
|
||
|
|
size_t start;
|
||
|
|
size_t end;
|
||
|
|
char *buf;
|
||
|
|
|
||
|
|
if (!cursor_skip_ws(cur) || cur->offset >= cur->len ||
|
||
|
|
cur->data[cur->offset] != '"') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
cur->offset++;
|
||
|
|
start = cur->offset;
|
||
|
|
while (cur->offset < cur->len && cur->data[cur->offset] != '"') {
|
||
|
|
cur->offset++;
|
||
|
|
}
|
||
|
|
if (cur->offset >= cur->len) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
end = cur->offset;
|
||
|
|
cur->offset++;
|
||
|
|
|
||
|
|
buf = (char *)malloc(end - start + 1u);
|
||
|
|
if (buf == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
memcpy(buf, cur->data + start, end - start);
|
||
|
|
buf[end - start] = '\0';
|
||
|
|
*out = buf;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool cursor_read_number(cursor_t *cur, size_t *out) {
|
||
|
|
size_t start;
|
||
|
|
size_t value = 0u;
|
||
|
|
bool has_digit = false;
|
||
|
|
|
||
|
|
if (!cursor_skip_ws(cur)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
start = cur->offset;
|
||
|
|
while (cur->offset < cur->len) {
|
||
|
|
char c = (char)cur->data[cur->offset];
|
||
|
|
if (c < '0' || c > '9') {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
has_digit = true;
|
||
|
|
value = value * 10u + (size_t)(c - '0');
|
||
|
|
cur->offset++;
|
||
|
|
}
|
||
|
|
if (!has_digit || start == cur->offset) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
*out = value;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool hex_decode(const char *hex, uint8_t **out, size_t *out_len) {
|
||
|
|
size_t len;
|
||
|
|
size_t i;
|
||
|
|
uint8_t *buf;
|
||
|
|
|
||
|
|
if (hex == NULL || out == NULL || out_len == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
len = strlen(hex);
|
||
|
|
if (len % 2u != 0u) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
buf = (uint8_t *)malloc(len / 2u);
|
||
|
|
if (buf == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
for (i = 0u; i < len; i += 2u) {
|
||
|
|
unsigned int byte;
|
||
|
|
if (sscanf(hex + i, "%2x", &byte) != 1) {
|
||
|
|
free(buf);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
buf[i / 2u] = (uint8_t)byte;
|
||
|
|
}
|
||
|
|
*out = buf;
|
||
|
|
*out_len = len / 2u;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool hex_eq(const char *hex, amduat_octets_t bytes) {
|
||
|
|
uint8_t *decoded = NULL;
|
||
|
|
size_t decoded_len = 0u;
|
||
|
|
bool ok = false;
|
||
|
|
|
||
|
|
if (!hex_decode(hex, &decoded, &decoded_len)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
ok = decoded_len == bytes.len &&
|
||
|
|
(decoded_len == 0u ||
|
||
|
|
memcmp(decoded, bytes.data, decoded_len) == 0);
|
||
|
|
free(decoded);
|
||
|
|
return ok;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool cid_matches(const char *hex, amduat_octets_t output) {
|
||
|
|
amduat_reference_t cid;
|
||
|
|
size_t hex_len = strlen(hex);
|
||
|
|
char *lower = NULL;
|
||
|
|
char *scratch = NULL;
|
||
|
|
bool ok = false;
|
||
|
|
|
||
|
|
if (!amduat_fps_cid_for_payload(AMDUAT_HASH_ASL1_ID_SHA256, output, &cid)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
lower = (char *)malloc(hex_len + 1u);
|
||
|
|
if (lower == NULL) {
|
||
|
|
amduat_reference_free(&cid);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
scratch = (char *)malloc(cid.digest.len * 2u + 1u);
|
||
|
|
if (scratch == NULL) {
|
||
|
|
free(lower);
|
||
|
|
amduat_reference_free(&cid);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
for (size_t i = 0u; i < hex_len; ++i) {
|
||
|
|
char c = hex[i];
|
||
|
|
if (c >= 'A' && c <= 'F') {
|
||
|
|
c = (char)(c - 'A' + 'a');
|
||
|
|
}
|
||
|
|
lower[i] = c;
|
||
|
|
}
|
||
|
|
lower[hex_len] = '\0';
|
||
|
|
for (size_t i = 0u; i < cid.digest.len; ++i) {
|
||
|
|
sprintf(scratch + i * 2u, "%02x", cid.digest.data[i]);
|
||
|
|
}
|
||
|
|
ok = strcmp(lower, scratch) == 0;
|
||
|
|
free(lower);
|
||
|
|
free(scratch);
|
||
|
|
amduat_reference_free(&cid);
|
||
|
|
return ok;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool parse_manifest_cases(cursor_t *cur) {
|
||
|
|
if (!cursor_skip_ws(cur) || cur->offset >= cur->len ||
|
||
|
|
cur->data[cur->offset] != '[') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
cur->offset++;
|
||
|
|
while (true) {
|
||
|
|
char *field = NULL;
|
||
|
|
char *value = NULL;
|
||
|
|
char *op = NULL;
|
||
|
|
char *input = NULL;
|
||
|
|
char *insert = NULL;
|
||
|
|
char *output = NULL;
|
||
|
|
char *cid_hex = NULL;
|
||
|
|
char *level = NULL;
|
||
|
|
char *inputs[4] = {0};
|
||
|
|
size_t inputs_len = 0u;
|
||
|
|
size_t offset = 0u;
|
||
|
|
size_t length = 0u;
|
||
|
|
bool has_offset = false;
|
||
|
|
bool has_length = false;
|
||
|
|
bool has_inputs = false;
|
||
|
|
bool has_input = false;
|
||
|
|
bool has_insert = false;
|
||
|
|
bool has_level = false;
|
||
|
|
bool has_output = false;
|
||
|
|
bool has_cid = false;
|
||
|
|
bool ok = true;
|
||
|
|
|
||
|
|
if (!cursor_skip_ws(cur)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (cur->offset < cur->len && cur->data[cur->offset] == ']') {
|
||
|
|
cur->offset++;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (cur->offset >= cur->len || cur->data[cur->offset] != '{') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
cur->offset++;
|
||
|
|
while (true) {
|
||
|
|
if (!cursor_read_string(cur, &field)) {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (!cursor_skip_ws(cur) || cur->offset >= cur->len ||
|
||
|
|
cur->data[cur->offset] != ':') {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
cur->offset++;
|
||
|
|
if (strcmp(field, "id") == 0 ||
|
||
|
|
strcmp(field, "op") == 0 || strcmp(field, "input") == 0 ||
|
||
|
|
strcmp(field, "insert") == 0 || strcmp(field, "output") == 0 ||
|
||
|
|
strcmp(field, "cid_sha256") == 0 || strcmp(field, "level") == 0) {
|
||
|
|
if (!cursor_read_string(cur, &value)) {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (strcmp(field, "id") == 0) {
|
||
|
|
free(value);
|
||
|
|
value = NULL;
|
||
|
|
} else if (strcmp(field, "op") == 0) {
|
||
|
|
op = value;
|
||
|
|
} else if (strcmp(field, "input") == 0) {
|
||
|
|
input = value;
|
||
|
|
has_input = true;
|
||
|
|
} else if (strcmp(field, "insert") == 0) {
|
||
|
|
insert = value;
|
||
|
|
has_insert = true;
|
||
|
|
} else if (strcmp(field, "output") == 0) {
|
||
|
|
output = value;
|
||
|
|
has_output = true;
|
||
|
|
} else if (strcmp(field, "cid_sha256") == 0) {
|
||
|
|
cid_hex = value;
|
||
|
|
has_cid = true;
|
||
|
|
} else {
|
||
|
|
level = value;
|
||
|
|
has_level = true;
|
||
|
|
}
|
||
|
|
} else if (strcmp(field, "offset") == 0 ||
|
||
|
|
strcmp(field, "length") == 0) {
|
||
|
|
size_t num = 0u;
|
||
|
|
if (!cursor_read_number(cur, &num)) {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (strcmp(field, "offset") == 0) {
|
||
|
|
offset = num;
|
||
|
|
has_offset = true;
|
||
|
|
} else {
|
||
|
|
length = num;
|
||
|
|
has_length = true;
|
||
|
|
}
|
||
|
|
} else if (strcmp(field, "inputs") == 0) {
|
||
|
|
if (!cursor_skip_ws(cur) || cur->offset >= cur->len ||
|
||
|
|
cur->data[cur->offset] != '[') {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
cur->offset++;
|
||
|
|
while (true) {
|
||
|
|
if (!cursor_skip_ws(cur)) {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (cur->offset < cur->len && cur->data[cur->offset] == ']') {
|
||
|
|
cur->offset++;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (inputs_len >= 4u) {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (!cursor_read_string(cur, &inputs[inputs_len++])) {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (!cursor_skip_ws(cur)) {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (cur->offset < cur->len && cur->data[cur->offset] == ',') {
|
||
|
|
cur->offset++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
has_inputs = true;
|
||
|
|
} else {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(field);
|
||
|
|
field = NULL;
|
||
|
|
if (!cursor_skip_ws(cur)) {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (cur->offset < cur->len && cur->data[cur->offset] == ',') {
|
||
|
|
cur->offset++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (cur->offset < cur->len && cur->data[cur->offset] == '}') {
|
||
|
|
cur->offset++;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!ok || op == NULL || !has_output || !has_cid) {
|
||
|
|
ok = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ok) {
|
||
|
|
amduat_octets_t output_bytes = amduat_octets(NULL, 0u);
|
||
|
|
amduat_octets_t out = amduat_octets(NULL, 0u);
|
||
|
|
amduat_octets_t input_bytes = amduat_octets(NULL, 0u);
|
||
|
|
amduat_octets_t insert_bytes = amduat_octets(NULL, 0u);
|
||
|
|
uint8_t *decoded = NULL;
|
||
|
|
size_t decoded_len = 0u;
|
||
|
|
uint8_t *insert_decoded = NULL;
|
||
|
|
size_t insert_len = 0u;
|
||
|
|
|
||
|
|
if (!hex_decode(output, &decoded, &decoded_len)) {
|
||
|
|
ok = false;
|
||
|
|
} else {
|
||
|
|
output_bytes = amduat_octets(decoded, decoded_len);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ok && !cid_matches(cid_hex, output_bytes)) {
|
||
|
|
ok = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ok && strcmp(op, "slice") == 0) {
|
||
|
|
if (!has_input || !has_offset || !has_length) {
|
||
|
|
ok = false;
|
||
|
|
} else if (!hex_decode(input, &decoded, &decoded_len)) {
|
||
|
|
ok = false;
|
||
|
|
} else {
|
||
|
|
input_bytes = amduat_octets(decoded, decoded_len);
|
||
|
|
if (amduat_fps_slice(input_bytes, offset, length, &out) !=
|
||
|
|
AMDUAT_FPS_OK) {
|
||
|
|
ok = false;
|
||
|
|
} else if (!hex_eq(output, out)) {
|
||
|
|
ok = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else if (ok && strcmp(op, "concat") == 0) {
|
||
|
|
if (!has_inputs) {
|
||
|
|
ok = false;
|
||
|
|
} else {
|
||
|
|
amduat_octets_t in_octets[4];
|
||
|
|
for (size_t i = 0u; i < inputs_len; ++i) {
|
||
|
|
if (!hex_decode(inputs[i], &decoded, &decoded_len)) {
|
||
|
|
ok = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
in_octets[i] = amduat_octets(decoded, decoded_len);
|
||
|
|
}
|
||
|
|
if (ok) {
|
||
|
|
if (amduat_fps_concat(in_octets, inputs_len, &out) !=
|
||
|
|
AMDUAT_FPS_OK) {
|
||
|
|
ok = false;
|
||
|
|
} else if (!hex_eq(output, out)) {
|
||
|
|
ok = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (size_t i = 0u; i < inputs_len; ++i) {
|
||
|
|
free((void *)in_octets[i].data);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else if (ok && strcmp(op, "reverse") == 0) {
|
||
|
|
amduat_fps_reverse_level_t lvl = AMDUAT_FPS_REVERSE_BYTES;
|
||
|
|
if (!has_input || !has_level) {
|
||
|
|
ok = false;
|
||
|
|
} else if (strcmp(level, "bit") == 0) {
|
||
|
|
lvl = AMDUAT_FPS_REVERSE_BITS;
|
||
|
|
} else if (strcmp(level, "byte") == 0) {
|
||
|
|
lvl = AMDUAT_FPS_REVERSE_BYTES;
|
||
|
|
} else if (strcmp(level, "word") == 0) {
|
||
|
|
lvl = AMDUAT_FPS_REVERSE_WORDS;
|
||
|
|
} else if (strcmp(level, "long") == 0) {
|
||
|
|
lvl = AMDUAT_FPS_REVERSE_LONGS;
|
||
|
|
} else {
|
||
|
|
ok = false;
|
||
|
|
}
|
||
|
|
if (ok && !hex_decode(input, &decoded, &decoded_len)) {
|
||
|
|
ok = false;
|
||
|
|
}
|
||
|
|
if (ok) {
|
||
|
|
input_bytes = amduat_octets(decoded, decoded_len);
|
||
|
|
if (amduat_fps_reverse(input_bytes, lvl, &out) != AMDUAT_FPS_OK) {
|
||
|
|
ok = false;
|
||
|
|
} else if (!hex_eq(output, out)) {
|
||
|
|
ok = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else if (ok && strcmp(op, "splice") == 0) {
|
||
|
|
if (!has_input || !has_insert || !has_offset) {
|
||
|
|
ok = false;
|
||
|
|
} else if (!hex_decode(input, &decoded, &decoded_len) ||
|
||
|
|
!hex_decode(insert, &insert_decoded, &insert_len)) {
|
||
|
|
ok = false;
|
||
|
|
} else {
|
||
|
|
input_bytes = amduat_octets(decoded, decoded_len);
|
||
|
|
insert_bytes = amduat_octets(insert_decoded, insert_len);
|
||
|
|
if (amduat_fps_splice(input_bytes, offset, insert_bytes, &out) !=
|
||
|
|
AMDUAT_FPS_OK) {
|
||
|
|
ok = false;
|
||
|
|
} else if (!hex_eq(output, out)) {
|
||
|
|
ok = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else if (ok) {
|
||
|
|
ok = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
amduat_octets_free(&out);
|
||
|
|
free((void *)output_bytes.data);
|
||
|
|
if (input_bytes.data != NULL) {
|
||
|
|
free((void *)input_bytes.data);
|
||
|
|
}
|
||
|
|
if (insert_bytes.data != NULL) {
|
||
|
|
free((void *)insert_bytes.data);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
free(op);
|
||
|
|
free(input);
|
||
|
|
free(insert);
|
||
|
|
free(output);
|
||
|
|
free(cid_hex);
|
||
|
|
free(level);
|
||
|
|
for (size_t i = 0u; i < inputs_len; ++i) {
|
||
|
|
free(inputs[i]);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!ok) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!cursor_skip_ws(cur)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (cur->offset < cur->len && cur->data[cur->offset] == ',') {
|
||
|
|
cur->offset++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (cur->offset < cur->len && cur->data[cur->offset] == ']') {
|
||
|
|
cur->offset++;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool parse_manifest(uint8_t *bytes, size_t len) {
|
||
|
|
cursor_t cur;
|
||
|
|
char *field = NULL;
|
||
|
|
char *schema = NULL;
|
||
|
|
bool ok = false;
|
||
|
|
|
||
|
|
cur.data = bytes;
|
||
|
|
cur.len = len;
|
||
|
|
cur.offset = 0u;
|
||
|
|
|
||
|
|
if (!cursor_skip_ws(&cur) || cur.offset >= cur.len ||
|
||
|
|
cur.data[cur.offset] != '{') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
cur.offset++;
|
||
|
|
|
||
|
|
while (true) {
|
||
|
|
if (!cursor_read_string(&cur, &field)) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (!cursor_skip_ws(&cur) || cur.offset >= cur.len ||
|
||
|
|
cur.data[cur.offset] != ':') {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
cur.offset++;
|
||
|
|
if (strcmp(field, "schema") == 0) {
|
||
|
|
if (!cursor_read_string(&cur, &schema)) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
} else if (strcmp(field, "cases") == 0) {
|
||
|
|
if (!parse_manifest_cases(&cur)) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(field);
|
||
|
|
field = NULL;
|
||
|
|
if (!cursor_skip_ws(&cur)) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (cur.offset < cur.len && cur.data[cur.offset] == ',') {
|
||
|
|
cur.offset++;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (cur.offset < cur.len && cur.data[cur.offset] == '}') {
|
||
|
|
cur.offset++;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (schema != NULL &&
|
||
|
|
strcmp(schema, "amduat.conformance.fps.v0") == 0) {
|
||
|
|
ok = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(field);
|
||
|
|
free(schema);
|
||
|
|
return ok;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void) {
|
||
|
|
char path[512];
|
||
|
|
uint8_t *bytes = NULL;
|
||
|
|
size_t len = 0u;
|
||
|
|
int exit_code = 1;
|
||
|
|
|
||
|
|
if (!build_fixture_path(path, sizeof(path))) {
|
||
|
|
fprintf(stderr, "fixture path build failed\n");
|
||
|
|
return exit_code;
|
||
|
|
}
|
||
|
|
if (!read_file(path, &bytes, &len)) {
|
||
|
|
fprintf(stderr, "fixture read failed: %s\n", path);
|
||
|
|
return exit_code;
|
||
|
|
}
|
||
|
|
if (!parse_manifest(bytes, len)) {
|
||
|
|
fprintf(stderr, "fixture parse failed\n");
|
||
|
|
goto cleanup;
|
||
|
|
}
|
||
|
|
|
||
|
|
exit_code = 0;
|
||
|
|
|
||
|
|
cleanup:
|
||
|
|
free(bytes);
|
||
|
|
return exit_code;
|
||
|
|
}
|