From ee30089152344a47151c29451490c8440387d60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Sch=C3=B6bel?= <jonathan@xn--schbel-yxa.info> Date: Fri, 29 Sep 2023 10:49:35 +0200 Subject: [PATCH] Validator: added initializer for attributes Similar to the tags, the attributes can be initialized. Missing tags are automatically added. The declaration syntax is currently a bit annoying, as the tags, that belong to an attribute, either have to be declared explicitly or a pointer to the tag declaration must be given, but then only concurrent tags are possible. Support for global attributes is likewise missing; it must be ensured, that (tag_n != 0) && (tags != NULL). Otherwise validator will be inconsistent and there might be a bug. --- sefht.geany | 10 +- src/lib/sefht/validator.c | 7 +- src/lib/sefht/validator_attr.c | 210 +++++++++++++++++++++++++++++++++ src/lib/sefht/validator_html.h | 10 ++ tests/test_validator_attr.c | 127 +++++++++++++++++++- 5 files changed, 356 insertions(+), 8 deletions(-) diff --git a/sefht.geany b/sefht.geany index cd85443..4f62b7f 100644 --- a/sefht.geany +++ b/sefht.geany @@ -28,7 +28,7 @@ long_line_behaviour=1 long_line_column=72 [files] -current_page=31 +current_page=54 FILE_NAME_0=139;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2FREADME;0;8 FILE_NAME_1=134;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2F.gitignore;0;8 FILE_NAME_2=1737;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fconfigure.ac;0;8 @@ -58,13 +58,13 @@ FILE_NAME_25=8479;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fp FILE_NAME_26=1083;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment.h;0;8 FILE_NAME_27=900;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment_mark.c;0;8 FILE_NAME_28=1867;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_mark_static.c;0;8 -FILE_NAME_29=3036;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator.c;0;8 +FILE_NAME_29=2191;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator.c;0;8 FILE_NAME_30=1159;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator.h;0;8 -FILE_NAME_31=1100;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_html.h;0;8 +FILE_NAME_31=1303;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_html.h;0;8 FILE_NAME_32=4198;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_tag.c;0;8 FILE_NAME_33=1068;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_tag.h;0;8 FILE_NAME_34=1287;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_tag_data.h;0;8 -FILE_NAME_35=16395;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_attr.c;0;8 +FILE_NAME_35=7484;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_attr.c;0;8 FILE_NAME_36=1051;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_attr.h;0;8 FILE_NAME_37=1413;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_attr_data.h;0;8 FILE_NAME_38=924;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fstatus.h;0;8 @@ -83,7 +83,7 @@ FILE_NAME_50=4221;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fp FILE_NAME_51=994;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text_mark.c;0;8 FILE_NAME_52=2447;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8 FILE_NAME_53=4963;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator_tag.c;0;8 -FILE_NAME_54=28881;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator_attr.c;0;8 +FILE_NAME_54=33431;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator_attr.c;0;8 FILE_NAME_55=548;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftodo.txt;0;8 FILE_NAME_56=201;YAML;0;EUTF-8;0;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2F.gitlab-ci.yml;0;4 FILE_NAME_57=71;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fgitlab-ci%2Fupload.sh.in;0;8 diff --git a/src/lib/sefht/validator.c b/src/lib/sefht/validator.c index fa6aecd..6a81c87 100644 --- a/src/lib/sefht/validator.c +++ b/src/lib/sefht/validator.c @@ -95,7 +95,12 @@ SH_Validator_new_html5 (/*@null@*/ /*@out@*/ struct SH_Status * status) return NULL; } - init_attrs (validator); + if (!init_attrs_spec_html5 (validator, status)) + { + free_tags (validator); + free (validator); + return NULL; + } set_success (status); return validator; diff --git a/src/lib/sefht/validator_attr.c b/src/lib/sefht/validator_attr.c index d8720eb..e4696a3 100644 --- a/src/lib/sefht/validator_attr.c +++ b/src/lib/sefht/validator_attr.c @@ -42,6 +42,51 @@ init_attrs (/*@special@*/ struct SH_Validator * validator) /*@modifies validator->attrs@*/ /*@modifies validator->attr_n@*/; +static inline +bool +init_attrs_spec_html5 (/*@special@*/ struct SH_Validator * validator, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@uses validator->tags, + validator->tag_n@*/ + /*@defines validator->attrs, + validator->attr_n@*/ + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + +static inline +bool +init_attr_from_spec (/*@special@*/ struct SH_Validator * validator, + /*@out@*/ struct attr_info * attr_data, + const struct HTML_ATTR_DEFINITION spec, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@uses validator->tags, + validator->tag_n@*/ + /*@modifies attr_data->name@*/ + /*@modifies attr_data->tags@*/ + /*@modifies attr_data->tag_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + +static inline +bool +init_attrs_from_spec (/*@special@*/ struct SH_Validator * validator, + const struct HTML_ATTR_DEFINITION * spec, + const size_t size, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@uses validator->tags, + validator->tag_n@*/ + /*@defines validator->attrs, + validator->attr_n@*/ + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + static inline bool copy_attrs (/*@special@*/ struct SH_Validator * copy, @@ -153,6 +198,171 @@ init_attrs (/*@special@*/ struct SH_Validator * validator) return TRUE; } +static inline +bool +init_attrs_spec_html5 (/*@special@*/ struct SH_Validator * validator, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@uses validator->tags, + validator->tag_n@*/ + /*@defines validator->attrs, + validator->attr_n@*/ + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + size_t size; + size = sizeof (HTML5_ATTRS) / sizeof (HTML5_ATTRS[0]); + return init_attrs_from_spec (validator, HTML5_ATTRS, size, status); +} + +static inline +bool +init_attr_from_spec (/*@special@*/ struct SH_Validator * validator, + /*@out@*/ struct attr_info * attr_data, + const struct HTML_ATTR_DEFINITION spec, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@uses validator->tags, + validator->tag_n@*/ + /*@modifies validator->tags@*/ + /*@modifies validator->tag_n@*/ + /*@modifies attr_data->name@*/ + /*@modifies attr_data->tags@*/ + /*@modifies attr_data->tag_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + attr_data->name = strdup (spec.attr); + if (NULL == attr_data->name) + { + set_status (status, E_ALLOC, 3, "strdup failed"); + return FALSE; + } + + if (spec.tag_n >= (SIZE_MAX / sizeof (struct attr_tag_info))) + { + set_status (status, E_DOMAIN, 2, + "maximum number of tags per attr reached"); + free (attr_data->name); + return FALSE; + } + + attr_data->tags = malloc (spec.tag_n + * sizeof (struct attr_tag_info)); + if (NULL == attr_data->tags) + { + set_status (status, E_ALLOC, 3, "malloc failed"); + free (attr_data->name); + return FALSE; + } + + attr_data->tag_n = 0; + + for (size_t index = 0; index < spec.tag_n; index++) + { + size_t position; + const char * tag; + + tag = spec.tags[index].tag; + if ((!find_tag (validator, tag, &position)) + && (!add_tag (validator, tag, position, status))) + { + free (attr_data->name); + free (attr_data->tags); + return FALSE; + } + tag = validator->tags[position].name; + + #define tags attr_data->tags + #define tag_n attr_data->tag_n + /* ignore duplicate tags */ + if (!find_attr_tag (attr_data, tag, &position)) + { + for (size_t i = tag_n; i > position; i--) + { + tags[i] = tags[i-1]; + } + tags[position].name = tag; + tag_n++; + } + #undef tag_n + #undef tags + } + + return TRUE; +} + +static inline +bool +init_attrs_from_spec (/*@special@*/ struct SH_Validator * validator, + const struct HTML_ATTR_DEFINITION * spec, + const size_t size, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@uses validator->tags, + validator->tag_n@*/ + /*@defines validator->attrs, + validator->attr_n@*/ + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + /* overflow detection */ + if (size >= (SIZE_MAX / sizeof (struct attr_info))) + { + set_status (status, E_DOMAIN, 2, + "maximum number of attrs reached"); + return FALSE; + } + + validator->attrs = malloc (size * sizeof (struct attr_info)); + if (NULL == validator->attrs) + { + set_status (status, E_ALLOC, 3, "malloc failed"); + return FALSE; + } + + + /* needs to be initialized, as find_attr uses it */ + validator->attr_n = 0; + + /* insertion sort */ + #define attrs validator->attrs + #define attr_n validator->attr_n + for (size_t index = 0; index < size; index++) + { + size_t position; + + /* ignore duplicate attrs */ + if (!find_attr (validator, spec[index].attr, &position)) + { + struct attr_info attr_data; + + if (!init_attr_from_spec (validator, &attr_data, + spec[index], status)) + { + free_attrs (validator); + return FALSE; + } + + for (size_t i = attr_n; i > position; i--) + { + attrs[i] = attrs[i-1]; + } + + attrs[position] = attr_data; + attr_n++; + } + } + #undef attr_n + #undef attrs + + return TRUE; +} + static inline bool copy_attr (/*@special@*/ struct attr_info * copy, diff --git a/src/lib/sefht/validator_html.h b/src/lib/sefht/validator_html.h index 54cdfc2..f952eb2 100644 --- a/src/lib/sefht/validator_html.h +++ b/src/lib/sefht/validator_html.h @@ -34,8 +34,18 @@ struct HTML_TAG_DEFINITION /*@observer@*/ const char * tag; }; +struct HTML_ATTR_DEFINITION +{ + /*@observer@*/ const char * attr; + /*@observer@*/ const struct HTML_TAG_DEFINITION * tags; + size_t tag_n; +}; + const struct HTML_TAG_DEFINITION HTML5_TAGS[] = { }; +const struct HTML_ATTR_DEFINITION HTML5_ATTRS[] = { +}; + #endif /* SEFHT_VALIDATOR_HTML_H */ diff --git a/tests/test_validator_attr.c b/tests/test_validator_attr.c index fd769a1..37e5490 100644 --- a/tests/test_validator_attr.c +++ b/tests/test_validator_attr.c @@ -38,6 +38,24 @@ #include "status.h" +/* override HTML spec */ +#include "validator_html.h" + +const struct HTML_TAG_DEFINITION HTML_TAGS[] = { + {"html"}, + {"aside"}, + {"a"}, +}; + +const struct HTML_ATTR_DEFINITION HTML_ATTRS[] = { + {"lang", &HTML_TAGS[0], 2}, + {"href", &HTML_TAGS[2], 1}, +}; + +#define HTML5_TAGS HTML_TAGS +#define HTML5_ATTRS HTML_ATTRS + + /* C file is needed, because we want to override SIZE_MAX */ #include "validator.c" @@ -174,6 +192,109 @@ START_TEST(test_validator_copy_with_status) } END_TEST +START_TEST(test_validator_spec_no_status) +{ + struct SH_Validator * validator; + + /* test */ + validator = SH_Validator_new_html5 (NULL); + ck_assert_ptr_ne (NULL, validator); + + ck_assert_ptr_ne (NULL, validator->attrs); + ck_assert_int_eq (2, validator->attr_n); + + #define attrs validator->attrs + #define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2); \ + ck_assert_str_eq (S1, S2); + + ck_assert_ptr_ne (HTML_ATTRS[1].attr, attrs[0].name); + ck_assert_str_eq (HTML_ATTRS[1].attr, attrs[0].name); + ck_assert_int_eq (HTML_ATTRS[1].tag_n, attrs[0].tag_n); + ck_assert_ptr_ne (NULL, attrs[0].tags); + + TEST_STR (HTML_TAGS[2].tag, attrs[0].tags[0].name); + + ck_assert_ptr_ne (HTML_ATTRS[0].attr, attrs[1].name); + ck_assert_str_eq (HTML_ATTRS[0].attr, attrs[1].name); + ck_assert_int_eq (HTML_ATTRS[0].tag_n, attrs[1].tag_n); + ck_assert_ptr_ne (NULL, attrs[1].tags); + + /* The storage order depends on the relative position of memory, + * allocated by different malloc calls. This can change and + * thus must be determined. */ + if (attrs[1].tags[0].name[0] + > attrs[1].tags[1].name[0]) + { + TEST_STR (HTML_TAGS[0].tag, attrs[1].tags[0].name); + TEST_STR (HTML_TAGS[1].tag, attrs[1].tags[1].name); + } + else + { + TEST_STR (HTML_TAGS[1].tag, attrs[1].tags[0].name); + TEST_STR (HTML_TAGS[0].tag, attrs[1].tags[1].name); + } + + #undef TEST_STR + #undef attrs + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_spec_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + + /* test */ + _status_preinit (status); + validator = SH_Validator_new_html5 (&status); + ck_assert_ptr_ne (NULL, validator); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_ptr_ne (NULL, validator->attrs); + ck_assert_int_eq (2, validator->attr_n); + + #define attrs validator->attrs + #define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2); \ + ck_assert_str_eq (S1, S2); + + ck_assert_ptr_ne (HTML_ATTRS[1].attr, attrs[0].name); + ck_assert_str_eq (HTML_ATTRS[1].attr, attrs[0].name); + ck_assert_int_eq (HTML_ATTRS[1].tag_n, attrs[0].tag_n); + ck_assert_ptr_ne (NULL, attrs[0].tags); + + TEST_STR (HTML_TAGS[2].tag, attrs[0].tags[0].name); + + ck_assert_ptr_ne (HTML_ATTRS[0].attr, attrs[1].name); + ck_assert_str_eq (HTML_ATTRS[0].attr, attrs[1].name); + ck_assert_int_eq (HTML_ATTRS[0].tag_n, attrs[1].tag_n); + ck_assert_ptr_ne (NULL, attrs[1].tags); + + /* The storage order depends on the relative position of memory, + * allocated by different malloc calls. This can change and + * thus must be determined. */ + if (attrs[1].tags[0].name[0] + > attrs[1].tags[1].name[0]) + { + TEST_STR (HTML_TAGS[0].tag, attrs[1].tags[0].name); + TEST_STR (HTML_TAGS[1].tag, attrs[1].tags[1].name); + } + else + { + TEST_STR (HTML_TAGS[1].tag, attrs[1].tags[0].name); + TEST_STR (HTML_TAGS[0].tag, attrs[1].tags[1].name); + } + + #undef TEST_STR + #undef attrs + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + START_TEST(test_validator_register_no_status) { struct SH_Validator * validator; @@ -191,7 +312,7 @@ START_TEST(test_validator_register_no_status) bool result; /* setup */ - validator = SH_Validator_new_html5 (NULL); + validator = SH_Validator_new (NULL); ck_assert_ptr_ne (NULL, validator); /* test - register */ @@ -347,7 +468,7 @@ START_TEST(test_validator_register_with_status) bool result; /* setup */ - validator = SH_Validator_new_html5 (NULL); + validator = SH_Validator_new (NULL); ck_assert_ptr_ne (NULL, validator); /* test - register */ @@ -940,6 +1061,8 @@ Suite * test_suite (void) tcase_add_test (tc_core, test_validator_with_status); tcase_add_test (tc_core, test_validator_copy_no_status); tcase_add_test (tc_core, test_validator_copy_with_status); + tcase_add_test (tc_core, test_validator_spec_no_status); + tcase_add_test (tc_core, test_validator_spec_with_status); tcase_add_test (tc_core, test_validator_register_no_status); tcase_add_test (tc_core, test_validator_register_with_status); tcase_add_test (tc_core, test_validator_deregister_no_status); -- GitLab