Template
feat(detect): define feature-check registry schema
This commit is contained in:
+125
-37
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "kdl/schema.h"
|
||||
|
||||
#include "detect/check_registry.h"
|
||||
#include "error.h"
|
||||
#include "kdl/ast.h"
|
||||
#include "kdl/value.h"
|
||||
@@ -438,16 +439,124 @@ done:
|
||||
return e;
|
||||
}
|
||||
|
||||
/* One check child of a feature: its kind has already been resolved by
|
||||
* the registry (st_check_kind_from_node). Enforce the kind's DSL shape
|
||||
* from the registry table - the required positional argument (a
|
||||
* non-empty unannotated string), at most the kind's optional arguments
|
||||
* (for library/pkg_config: the literal `version` keyword followed by a
|
||||
* non-empty unannotated constraint string, mirroring the project
|
||||
* node's spelling), no properties, and no children. */
|
||||
static struct st_error *
|
||||
validate_check(const struct st_kdl_node *c, enum st_check_kind kind,
|
||||
const char *feature)
|
||||
{
|
||||
const struct st_check_shape *shape = st_check_kind_shape(kind);
|
||||
char msg[384];
|
||||
char cname[65];
|
||||
char cctx[192];
|
||||
struct st_error *e;
|
||||
struct st_kdl_arg *a;
|
||||
size_t nargs = 1;
|
||||
size_t max_args;
|
||||
size_t i;
|
||||
char *tmp = NULL;
|
||||
|
||||
name_into(&c->name, cname, sizeof cname);
|
||||
|
||||
if (c->args == NULL) {
|
||||
snprintf(msg, sizeof msg,
|
||||
"feature '%s': check '%s' requires an argument (the %s)",
|
||||
feature, cname, shape->arg_meaning);
|
||||
return err_at_owned(c->name.span, msg);
|
||||
}
|
||||
for (a = c->args->next; a != NULL; a = a->next) {
|
||||
nargs++;
|
||||
}
|
||||
max_args = shape->required_args + shape->optional_args;
|
||||
if (nargs > max_args) {
|
||||
/* span the first argument beyond the kind's shape */
|
||||
a = c->args;
|
||||
for (i = 0; i < max_args; i++) {
|
||||
a = a->next;
|
||||
}
|
||||
if (shape->optional_args == 0) {
|
||||
snprintf(msg, sizeof msg,
|
||||
"feature '%s': check '%s' takes exactly one argument",
|
||||
feature, cname);
|
||||
} else {
|
||||
snprintf(msg, sizeof msg,
|
||||
"feature '%s': check '%s' takes at most three arguments "
|
||||
"(the %s, then the optional 'version' keyword and its "
|
||||
"constraint)", feature, cname, shape->arg_meaning);
|
||||
}
|
||||
return err_at_owned(a->value.span, msg);
|
||||
}
|
||||
snprintf(cctx, sizeof cctx, "feature '%s': check '%s' argument",
|
||||
feature, cname);
|
||||
e = require_string_arg(&c->args->value, c->args->annotation, cctx,
|
||||
&tmp);
|
||||
free(tmp);
|
||||
if (e != NULL) {
|
||||
return e;
|
||||
}
|
||||
if (shape->optional_args == 2) {
|
||||
a = c->args->next;
|
||||
if (a != NULL) {
|
||||
if (a->annotation != NULL ||
|
||||
!(a->value.kind == ST_TOK_IDENT && a->value.len == 7 &&
|
||||
memcmp(a->value.text, "version", 7) == 0)) {
|
||||
snprintf(msg, sizeof msg,
|
||||
"feature '%s': check '%s': expected the keyword "
|
||||
"'version' as the second argument", feature, cname);
|
||||
return err_at_owned(a->value.span, msg);
|
||||
}
|
||||
if (a->next == NULL) {
|
||||
snprintf(msg, sizeof msg,
|
||||
"feature '%s': check '%s': 'version' requires the "
|
||||
"version constraint argument", feature, cname);
|
||||
return err_at_owned(a->value.span, msg);
|
||||
}
|
||||
{
|
||||
char vctx[192];
|
||||
|
||||
snprintf(vctx, sizeof vctx,
|
||||
"feature '%s': check '%s' version", feature,
|
||||
cname);
|
||||
e = require_string_arg(&a->next->value,
|
||||
a->next->annotation, vctx, &tmp);
|
||||
free(tmp);
|
||||
if (e != NULL) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c->props != NULL) {
|
||||
char key[65];
|
||||
|
||||
name_into(&c->props->key, key, sizeof key);
|
||||
snprintf(msg, sizeof msg,
|
||||
"feature '%s': check '%s' has unexpected property '%s' "
|
||||
"(checks take no properties)", feature, cname, key);
|
||||
return err_at_owned(c->props->key.span, msg);
|
||||
}
|
||||
if (c->children != NULL) {
|
||||
snprintf(msg, sizeof msg,
|
||||
"feature '%s': check '%s' takes no children", feature, cname);
|
||||
return err_at_owned(c->children->name.span, msg);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* feature "name" { <checks> }: the name shape (no properties) plus a
|
||||
* children block of CHECK nodes, validated STRUCTURALLY ONLY. The 8
|
||||
* check kinds and their exact argument/property shapes are todo 10's job;
|
||||
* until its registry lands, every child is accepted provided it has at
|
||||
* least one non-empty string argument and no children (its properties
|
||||
* are deliberately left for todo 10 to validate). */
|
||||
* children block of CHECK nodes. Each child's NAME is dispatched through
|
||||
* the feature-check registry (src/detect/check_registry.h, todo 10) to
|
||||
* one of the 8 kinds - header, function, library, type, sizeof,
|
||||
* program, compiler_flag, pkg_config - and its argument/property shape
|
||||
* is enforced from the registry table (see validate_check). */
|
||||
static struct st_error *
|
||||
validate_feature(const struct st_kdl_node *n)
|
||||
{
|
||||
char msg[192];
|
||||
char name[65];
|
||||
struct st_error *e;
|
||||
char *name_str = NULL;
|
||||
@@ -461,6 +570,7 @@ validate_feature(const struct st_kdl_node *n)
|
||||
|
||||
if (n->props != NULL) {
|
||||
char key[65];
|
||||
char msg[192];
|
||||
|
||||
name_into(&n->props->key, key, sizeof key);
|
||||
snprintf(msg, sizeof msg, "feature '%s' has unexpected property "
|
||||
@@ -471,44 +581,22 @@ validate_feature(const struct st_kdl_node *n)
|
||||
|
||||
for (c = n->children; c != NULL; c = c->next) {
|
||||
/*
|
||||
* HOOK (todo 10): dispatch on the check node NAME through the
|
||||
* HOOK (todo 10): dispatch the check node NAME through the
|
||||
* feature-check registry (src/detect/check_registry.h) to
|
||||
* validate the 8 kinds - header, function, library, type,
|
||||
* sizeof, program, compiler_flag, pkg_config - and their
|
||||
* argument/property shapes. Until then, structural checks only:
|
||||
* argument/property shapes.
|
||||
*/
|
||||
if (c->args == NULL) {
|
||||
char cname[65];
|
||||
enum st_check_kind kind;
|
||||
struct st_error *ke = NULL;
|
||||
|
||||
name_into(&c->name, cname, sizeof cname);
|
||||
snprintf(msg, sizeof msg,
|
||||
"feature '%s': check '%s' requires an argument", name,
|
||||
cname);
|
||||
e = err_at_owned(c->name.span, msg);
|
||||
kind = st_check_kind_from_node(c, &ke);
|
||||
if (ke != NULL) {
|
||||
e = ke;
|
||||
goto done;
|
||||
}
|
||||
{
|
||||
char cname[65];
|
||||
char cctx[192];
|
||||
char *tmp = NULL;
|
||||
|
||||
name_into(&c->name, cname, sizeof cname);
|
||||
snprintf(cctx, sizeof cctx, "feature '%s': check '%s' argument",
|
||||
name, cname);
|
||||
e = require_string_arg(&c->args->value, c->args->annotation,
|
||||
cctx, &tmp);
|
||||
free(tmp);
|
||||
if (e != NULL) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (c->children != NULL) {
|
||||
char cname[65];
|
||||
|
||||
name_into(&c->name, cname, sizeof cname);
|
||||
snprintf(msg, sizeof msg,
|
||||
"feature '%s': check '%s' takes no children", name, cname);
|
||||
e = err_at_owned(c->children->name.span, msg);
|
||||
e = validate_check(c, kind, name);
|
||||
if (e != NULL) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user