diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 778de229f1f57567ccf859a5c1d071199daefa59..9c5597de7c74fd46eda23fb7b247cff57c985644 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,6 +20,7 @@ setup: before_script: - apt update && apt install -y autoconf + script: - autoreconf -vi @@ -46,6 +47,7 @@ config: before_script: - apt update && apt install -y check + script: - mkdir build - cd build @@ -75,6 +77,7 @@ compile: before_script: - apt update && apt install -y make + script: - cd build/ - make @@ -102,9 +105,15 @@ test: before_script: - apt update && apt install -y check + - for file in `find . -type f -not -path "./.git/*"`; + do git rev-list -n 1 HEAD $file + | xargs git show -s --format=%ai + | awk -v file=$file + '{system("touch -d \"" $1 " " $2 " " $3 "\" " file)}'; + done + script: - cd build/ - - find . -type f -execdir touch '{}' + - make check dist: @@ -115,6 +124,13 @@ dist: before_script: - apt update && apt install -y make check + - for file in `find . -type f -not -path "./.git/*"`; + do git rev-list -n 1 HEAD $file + | xargs git show -s --format=%ai + | awk -v file=$file + '{system("touch -d \"" $1 " " $2 " " $3 "\" " file)}'; + done + script: - cd build/ - make distcheck diff --git a/gitlab-ci/release.sh.in b/gitlab-ci/release.sh.in index 85d6a966fc8c056d29ff54ae6c35f76961861b4e..be895dcf7cfe5827d5e66f53ea70ae1e95a8c6a8 100644 --- a/gitlab-ci/release.sh.in +++ b/gitlab-ci/release.sh.in @@ -14,9 +14,9 @@ DESCRIPTION="Nightly release for commit '$CI_COMMIT_SHA' ===================================================================== This release was created automatically. -Please do NOT use the automatically generated assets, as they don't\ -contain all the sourcecode of the project. Sadly gitlab doesn't allow\ -to turn them off. +Please do NOT use the automatically generated assets,<br /> +as they don't contain all the sourcecode of the project.<br /> +Sadly gitlab doesn't allow to turn them off.<br /> However the associated tarball contains a complete distribution." release-cli create --tag-name "$TAG_NAME" --description="$DESCRIPTION" \ diff --git a/sefht.geany b/sefht.geany index e646b9ce1409c89cd36c960f20fa217ff08f9eee..2dda1faf5bf39e3abb46a7b90b3421975371295c 100644 --- a/sefht.geany +++ b/sefht.geany @@ -28,22 +28,22 @@ long_line_behaviour=1 long_line_column=72 [files] -current_page=43 +current_page=9 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 FILE_NAME_3=73;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2FMakefile.am;0;8 FILE_NAME_4=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fmain.c;0;8 -FILE_NAME_5=956;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2FMakefile.am;0;8 +FILE_NAME_5=1867;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2FMakefile.am;0;8 FILE_NAME_6=18;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fcms.c;0;8 FILE_NAME_7=18;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fcms.h;0;8 -FILE_NAME_8=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fdata.c;0;8 -FILE_NAME_9=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fdata.h;0;8 +FILE_NAME_8=6482;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fdata.c;0;8 +FILE_NAME_9=2172;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fdata.h;0;8 FILE_NAME_10=23;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ffragment.c;0;8 FILE_NAME_11=23;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ffragment.h;0;8 FILE_NAME_12=28;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ffragment_data.c;0;8 FILE_NAME_13=29;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ffragment_class.c;0;8 -FILE_NAME_14=28;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fnode_fragment.c;0;8 +FILE_NAME_14=4785;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fnode_fragment.c;0;8 FILE_NAME_15=28;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fnode_fragment.h;0;8 FILE_NAME_16=6328;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_fragment.c;0;8 FILE_NAME_17=28;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_fragment.h;0;8 @@ -58,29 +58,36 @@ 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=24;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=24;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=28;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_32=28;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_33=924;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fstatus.h;0;8 -FILE_NAME_34=18;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Flog.h;0;4 -FILE_NAME_35=20;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fmacro.h;0;8 -FILE_NAME_36=20;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fsefht.h;0;8 -FILE_NAME_37=636;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2FMakefile.am;0;8 -FILE_NAME_38=218;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Fno_test.sh.in;0;8 -FILE_NAME_39=23;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_cms.c;0;8 -FILE_NAME_40=24;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_data.c;0;8 -FILE_NAME_41=8232;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_fragment.c;0;8 -FILE_NAME_42=33;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_node_fragment.c;0;8 -FILE_NAME_43=5714;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text_fragment.c;0;8 -FILE_NAME_44=24;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_attr.c;0;8 -FILE_NAME_45=4221;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text.c;0;8 -FILE_NAME_46=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_47=29;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8 -FILE_NAME_48=536;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftodo.txt;0;8 -FILE_NAME_49=201;YAML;0;EUTF-8;0;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2F.gitlab-ci.yml;0;4 -FILE_NAME_50=71;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fgitlab-ci%2Fupload.sh.in;0;8 -FILE_NAME_51=806;Sh;0;EUTF-8;0;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fgitlab-ci%2Frelease.sh.in;0;4 +FILE_NAME_29=3229;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=4857;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=13132;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=1992;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=1150;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=21193;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=1834;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 +FILE_NAME_39=18;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Flog.h;0;4 +FILE_NAME_40=20;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fmacro.h;0;8 +FILE_NAME_41=20;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fsefht.h;0;8 +FILE_NAME_42=3007;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2FMakefile.am;0;8 +FILE_NAME_43=218;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Fno_test.sh.in;0;8 +FILE_NAME_44=23;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_cms.c;0;8 +FILE_NAME_45=24;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_data.c;0;8 +FILE_NAME_46=8232;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_fragment.c;0;8 +FILE_NAME_47=4815;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_node_fragment.c;0;8 +FILE_NAME_48=5714;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text_fragment.c;0;8 +FILE_NAME_49=24;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_attr.c;0;8 +FILE_NAME_50=4221;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text.c;0;8 +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=926;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8 +FILE_NAME_53=20787;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=58751;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=497;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftodo.txt;0;8 +FILE_NAME_56=2938;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 +FILE_NAME_58=683;Sh;0;EUTF-8;0;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fgitlab-ci%2Frelease.sh.in;0;4 [VTE] last_dir=/home/jonathan/Documents/projects/prgm/internet/web/SeFHT/tests diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index d2f37890715f80e873e979ed21f74e1d6cb2e2a5..126dfd9503ef2519c32bea603086ffbfd209e902 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -26,7 +26,11 @@ EXTRA_DIST += sefht/text_data.h EXTRA_DIST += sefht/text_mark_static.c EXTRA_DIST += sefht/text_segment.c sefht/text_segment.h EXTRA_DIST += sefht/text_segment_mark.c +EXTRA_DIST += sefht/validator_html.h EXTRA_DIST += sefht/validator_tag.c +EXTRA_DIST += sefht/validator_tag_data.h +EXTRA_DIST += sefht/validator_attr.c +EXTRA_DIST += sefht/validator_attr_data.h nobase_include_HEADERS = nobase_include_HEADERS += sefht/sefht.h @@ -42,6 +46,7 @@ nobase_include_HEADERS += sefht/text_fragment.h nobase_include_HEADERS += sefht/text.h nobase_include_HEADERS += sefht/validator.h nobase_include_HEADERS += sefht/validator_tag.h +nobase_include_HEADERS += sefht/validator_attr.h libsefht_la_CPPFLAGS = -DSEFHT_COMPILATION libsefht_la_LDFLAGS = -version_info 0:0:0 diff --git a/src/lib/sefht/data.c b/src/lib/sefht/data.c index 413c5fd8c613117c361c9c2bde726cc8ccad941f..3f4e43280dbd86845ea423ecb84e8363e67a47ea 100644 --- a/src/lib/sefht/data.c +++ b/src/lib/sefht/data.c @@ -161,38 +161,12 @@ init_validator (struct SH_Data * data, /*@modifies fileSystem@*/ /*@modifies status@*/ { - data->validator = SH_Validator_new (status); - - if (data->validator == NULL) + data->validator = SH_Validator_new_html5 (status); + if (NULL == data->validator) { return FALSE; } - SH_Validator_register_tag (data->validator, "html", NULL); - SH_Validator_register_tag (data->validator, "head", NULL); - SH_Validator_register_tag (data->validator, "body", NULL); - SH_Validator_register_tag (data->validator, "meta", NULL); - SH_Validator_register_tag (data->validator, "link", NULL); - SH_Validator_register_tag (data->validator, "title", NULL); - SH_Validator_register_tag (data->validator, "main", NULL); - SH_Validator_register_tag (data->validator, "article", NULL); - SH_Validator_register_tag (data->validator, "section", NULL); - SH_Validator_register_tag (data->validator, "header", NULL); - SH_Validator_register_tag (data->validator, "footer", NULL); - SH_Validator_register_tag (data->validator, "h1", NULL); - SH_Validator_register_tag (data->validator, "h2", NULL); - SH_Validator_register_tag (data->validator, "h3", NULL); - SH_Validator_register_tag (data->validator, "h4", NULL); - SH_Validator_register_tag (data->validator, "h5", NULL); - SH_Validator_register_tag (data->validator, "h6", NULL); - SH_Validator_register_tag (data->validator, "p", NULL); - SH_Validator_register_tag (data->validator, "br", NULL); - SH_Validator_register_tag (data->validator, "i", NULL); - SH_Validator_register_tag (data->validator, "b", NULL); - SH_Validator_register_tag (data->validator, "strong", NULL); - SH_Validator_register_tag (data->validator, "em", NULL); - SH_Validator_register_tag (data->validator, "small", NULL); - return TRUE; } @@ -304,3 +278,30 @@ SH_Data_register_page (struct SH_Data * data, const char * name, return data->last_page; } + + +bool +SH_Data_check_tag (struct SH_Data * data, const char * tag) +{ + return SH_Validator_check_tag (data->validator, tag); +} + +bool +SH_Data_check_attr (struct SH_Data * data, + /*@null@*/ const char * tag, const char * attr) +{ + return SH_Validator_check_attr (data->validator, tag, attr); +} + +bool +SH_Data_is_self_closing_tag (const SH_Data * data, + const char * tag, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + return SH_Validator_is_self_closing_tag (data->validator, tag, + status); +} diff --git a/src/lib/sefht/data.h b/src/lib/sefht/data.h index 86c74c2403503d305eb7118c1892e228df3e618f..1c73c589436e0a8cfebd7cbb6eabfcad06afbbc8 100644 --- a/src/lib/sefht/data.h +++ b/src/lib/sefht/data.h @@ -65,4 +65,23 @@ SH_Data_register_page (SH_Data * data, const char * name, /*@modifies fileSystem@*/ /*@modifies status@*/; + +bool +SH_Data_check_tag (SH_Data * data, const char * tag) + /*@*/; + +bool +SH_Data_check_attr (SH_Data * data, + /*@null@*/ const char * tag, const char * attr) + /*@*/; + +bool +SH_Data_is_self_closing_tag (const SH_Data * data, + const char * tag, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + #endif /* SEFHT_DATA_H */ diff --git a/src/lib/sefht/node_fragment.c b/src/lib/sefht/node_fragment.c index f87f179cb8aaed05ff40ae2bbd96ee78e93b838f..0f98972d10a2fcb92f07db5c4fd8697fcf884f25 100644 --- a/src/lib/sefht/node_fragment.c +++ b/src/lib/sefht/node_fragment.c @@ -152,6 +152,14 @@ SH_NodeFragment_new (const char * tag, { struct SH_NodeFragment * fragment; + if (!SH_Data_check_tag (data, tag)) + { + set_status_ (status, E_VALUE, 2, strlen (tag), + "Tag %s isn't valid.\n", tag); + + return NULL; + } + fragment = malloc (sizeof (struct SH_NodeFragment)); if (fragment == NULL) { @@ -203,6 +211,14 @@ SH_NodeFragment_raw_new (/*@only@*/ char * tag, { struct SH_NodeFragment * fragment; + if (!SH_Data_check_tag (data, tag)) + { + set_status_ (status, E_VALUE, 2, strlen (tag), + "Tag %s isn't valid.\n", tag); + + return NULL; + } + fragment = malloc (sizeof (struct SH_NodeFragment)); if (fragment == NULL) { diff --git a/src/lib/sefht/validator.c b/src/lib/sefht/validator.c index 5b97aeaa562e4d62fd55c0d426601d6686fdf719..741e591e8cd358ffd0bed1ce3654dc541b60dccd 100644 --- a/src/lib/sefht/validator.c +++ b/src/lib/sefht/validator.c @@ -30,22 +30,18 @@ #include "validator.h" -/* "validator_tag.c" is included twice, - * because TAG_DATA must be defined, - * before SH_VAlidator can be defined, - * but SH_Validator must be defined, - * before the functions in "validator_tag.c" - * can use the definition, which themselves - * are needed before the functions in this - * file can be defined. */ -#include "validator_tag.c" +#include "validator_tag_data.h" +#include "validator_attr_data.h" + struct SH_Validator { TAG_DATA + ATTR_DATA }; -#define VALIDATOR_IS_DEFINED + #include "validator_tag.c" +#include "validator_attr.c" /*@null@*/ @@ -57,29 +53,52 @@ SH_Validator_new (/*@null@*/ /*@out@*/ struct SH_Status * status) /*@modifies status@*/ { struct SH_Validator * validator; + validator = malloc (sizeof (struct SH_Validator)); + if (NULL == validator) + { + set_status (status, E_ALLOC, 3, "malloc failed"); + return NULL; + } + + init_tags (validator); + init_attrs (validator); + + set_success (status); + return validator; +} - if (validator == NULL) +/*@null@*/ +/*@only@*/ +struct SH_Validator * +SH_Validator_new_html5 (/*@null@*/ /*@out@*/ struct SH_Status * status) + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + struct SH_Validator * validator; + + validator = malloc (sizeof (struct SH_Validator)); + if (NULL == validator) { - set_status (status, E_ALLOC, 4, - "Memory allocation for " - "SH_Validator failed.\n"); + set_status (status, E_ALLOC, 4, "malloc failed"); + return NULL; + } + if (!init_tags_spec_html5 (validator, status)) + { + free (validator); return NULL; } - if (!init_tags (validator, status)) + if (!init_attrs_spec_html5 (validator, status)) { -/* dangerous call to silence splint, should never be executed. */ -#ifdef S_SPLINT_S - free (validator->tags); -#endif + free_tags (validator); free (validator); return NULL; } set_success (status); - return validator; } @@ -93,14 +112,11 @@ SH_Validator_copy (const struct SH_Validator * validator, /*@modifies status@*/ { struct SH_Validator * copy; - copy = malloc (sizeof (struct SH_Validator)); - if (copy == NULL) + copy = malloc (sizeof (struct SH_Validator)); + if (NULL == copy) { - set_status (status, E_ALLOC, 4, - "Memory allocation for " - "SH_Validator failed.\n"); - + set_status (status, E_ALLOC, 3, "malloc failed"); return NULL; } @@ -114,8 +130,18 @@ SH_Validator_copy (const struct SH_Validator * validator, return NULL; } - set_success (status); + if (!copy_attrs (copy, validator, status)) + { + free_tags (copy); +/* dangerous call to silence splint, should never be executed. */ +#ifdef S_SPLINT_S + free (copy->attrs); +#endif + free (copy); + return NULL; + } + set_success (status); return copy; } @@ -125,7 +151,7 @@ SH_Validator_free (/*@only@*/ struct SH_Validator * validator) /*@releases validator@*/ { free_tags (validator); + free_attrs (validator); free (validator); - return; } diff --git a/src/lib/sefht/validator.h b/src/lib/sefht/validator.h index 6f5272656f03c29a5f6fa5885684c6bb6f833557..6e5fa175b3462416b7d7b9535bf743df22515415 100644 --- a/src/lib/sefht/validator.h +++ b/src/lib/sefht/validator.h @@ -36,6 +36,7 @@ typedef struct SH_Validator SH_Validator; #define __VALIDATOR_H_INSIDE__ #include "validator_tag.h" +#include "validator_attr.h" #undef __VALIDATOR_H_INSIDE__ @@ -47,6 +48,14 @@ SH_Validator_new (/*@null@*/ /*@out@*/ struct SH_Status * status) /*@modifies fileSystem@*/ /*@modifies status@*/; +/*@null@*/ +/*@only@*/ +SH_Validator * +SH_Validator_new_html5 (/*@null@*/ /*@out@*/ struct SH_Status * status) + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + /*@null@*/ /*@only@*/ SH_Validator * diff --git a/src/lib/sefht/validator_attr.c b/src/lib/sefht/validator_attr.c new file mode 100644 index 0000000000000000000000000000000000000000..7a09483ceeb5469c349cce839fe6d6dc7e60aa58 --- /dev/null +++ b/src/lib/sefht/validator_attr.c @@ -0,0 +1,1109 @@ +/* + * validator_attr.c + * + * Copyright 2023 Jonathan Schöbel <jonathan@xn--schbel-yxa.info> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + */ + + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "macro.h" +#include "log.h" +#include "status.h" + +#include "validator_attr.h" + + +static inline +bool +init_attrs (/*@special@*/ struct SH_Validator * validator) + /*@defines validator->attrs, + validator->attr_n@*/ + /*@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, + const struct SH_Validator * validator, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@defines copy->attrs, + copy->attr_n@*/ + /*@modifies copy->attrs@*/ + /*@modifies copy->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + +static inline +void +free_attr (struct attr_info attr) + /*@modifies attr.name@*/ + /*@releases attr.name@*/; + +static inline +void +free_attrs (/*@special@*/ struct SH_Validator * validator) + /*@modifies validator->attrs@*/ + /*@releases validator->attrs@*/; + +static inline +bool +find_attr (const struct SH_Validator * validator, + const char * attr, + /*@out@*/ size_t * index) + /*@modifies index@*/; + +static inline +bool +find_attr_tag (const struct attr_info * attr, + /*@dependent@*/ const char * tag, + /*@out@*/ size_t * index) + /*@modifies index@*/; + +static inline +bool +add_attr (struct SH_Validator * validator, + const char * attr, + /*@null@*/ /*@dependent@*/ const char * tag, + size_t index, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + +static inline +bool +remove_attr (struct SH_Validator * validator, + const size_t index, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + + +static inline +bool +attr_add_tag (struct attr_info * attr, + const size_t index, + /*@null@*/ /*@dependent@*/ const char * tag, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies attr@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + +static inline +bool +attr_remove_tag (struct attr_info * attr, + const size_t index, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies attr@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + +static inline +bool +remove_tag_for_all_attrs (struct SH_Validator * validator, + /*@dependent@*/ const char * tag, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + +static inline +bool +attr_global_remove_tag (struct attr_info * attr, + const struct tag_info * tags, + const size_t tag_n, + const char * tag, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies attr->tags@*/ + /*@modifies attr->tag_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + + +static inline +bool +init_attrs (/*@special@*/ struct SH_Validator * validator) + /*@defines validator->attrs, + validator->attr_n@*/ + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ +{ + validator->attrs = malloc (0); + validator->attr_n = 0; + 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 (0 == spec.tag_n) + { + attr_data->tags = NULL; + attr_data->tag_n = 0; + return TRUE; + } + + 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)) + { + set_status (status, E_STATE, 2, "no such tag"); + 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++; + } + else if ((0 == spec[index].tag_n) + && (0 != attrs[position].tag_n)) + { + free (attrs[position].tags); + attrs[position].tags = NULL; + attrs[position].tag_n = 0; + } + } + #undef attr_n + #undef attrs + + return TRUE; +} + +static inline +bool +copy_attr (/*@special@*/ struct attr_info * copy, + const struct attr_info * attr, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@defines copy->name, + copy->tag_n, + copy->tags@*/ + /*@modifies copy->name@*/ + /*@modifies copy->tag_n@*/ + /*@modifies copy->tags@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + size_t index; + char * name; + struct attr_tag_info * tags; + + name = strdup (attr->name); + if (NULL == name) + { + set_status (status, E_ALLOC, 3, "strdup failed"); + return FALSE; + } + + if (0 == attr->tag_n) + { + tags = NULL; + } + else + { + tags = malloc (attr->tag_n + * sizeof (struct attr_tag_info)); + if (NULL == tags) + { + set_status (status, E_ALLOC, 3, "malloc failed"); + free (name); + return FALSE; + } + } + + copy->name = name; + copy->tag_n = attr->tag_n; + copy->tags = tags; + for (index = 0; index < attr->tag_n; index++) + { + copy->tags[index] = attr->tags[index]; + } + + return TRUE; +} + +static inline +bool +copy_attrs (/*@special@*/ struct SH_Validator * copy, + const struct SH_Validator * validator, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@defines copy->attrs, + copy->attr_n@*/ + /*@modifies copy->attrs@*/ + /*@modifies copy->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + size_t index; + + /* The size calculation is save, + * because validator is already allocated. */ + copy->attrs = malloc (validator->attr_n + * sizeof (struct attr_info)); + + if (NULL == copy->attrs) + { + set_status (status, E_ALLOC, 5, "malloc failed"); + return FALSE; + } + + /* copy data */ + for (index = 0; index < validator->attr_n; index++) + { + if (!copy_attr (©->attrs[index], + &validator->attrs[index], status)) + { + copy->attr_n = index; + free_attrs (copy); + return FALSE; + } + } + + copy->attr_n = validator->attr_n; + + return TRUE; +} + +static inline +void +free_attr (struct attr_info attr) + /*@modifies attr.name@*/ + /*@releases attr.name@*/ +{ + free (attr.name); + if (0 != attr.tag_n) free (attr.tags); + return; +} + +static inline +void +free_attrs (/*@special@*/ struct SH_Validator * validator) + /*@modifies validator->attrs@*/ + /*@releases validator->attrs@*/ +{ + size_t index; + + for (index = 0; index < validator->attr_n; index++) + { + free_attr (validator->attrs[index]); + } + + free (validator->attrs); + return; +} + +static inline +bool +find_attr (const struct SH_Validator * validator, + const char * attr, + /*@out@*/ size_t * index) + /*@modifies index@*/ +{ + size_t start; + size_t end; + size_t pivot; + + start = 0; + end = validator->attr_n; + pivot = (end - start) / 2; + + while (start != end) + { + int cmp = strcmp (attr, validator->attrs[pivot].name); + + if (0 > cmp) + { + end = pivot; + pivot = (pivot - start) / 2 + start; + } + else if (0 == cmp) + { + *index = pivot; + return TRUE; + } + else + { + start = pivot + 1; + pivot = (end - pivot) / 2 + pivot; + } + } + + *index = start; + return FALSE; +} + +static inline +bool +find_attr_tag (const struct attr_info * attr, + /*@dependent@*/ const char * tag, + /*@out@*/ size_t * index) + /*@modifies index@*/ +{ + size_t start; + size_t end; + size_t pivot; + + start = 0; + end = attr->tag_n; + pivot = (end - start) / 2; + + while (start != end) + { + if (tag < attr->tags[pivot].name) + { + end = pivot; + pivot = (pivot - start) / 2 + start; + } + else if (tag == attr->tags[pivot].name) + { + *index = pivot; + return TRUE; + } + else + { + start = pivot + 1; + pivot = (end - pivot) / 2 + pivot; + } + } + + *index = start; + return FALSE; +} + +static inline +bool +add_attr (struct SH_Validator * validator, + const char * attr, + /*@null@*/ /*@dependent@*/ const char * tag, + size_t index, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + struct attr_info attr_data; + struct attr_info * new_attrs; + + /* abort on overflow: + * - no unused Attr or + * - no unused index */ + if ((validator->attr_n == SIZE_MAX) + || ((validator->attr_n + 1) + > (SIZE_MAX / sizeof (struct attr_info)))) + { + set_status (status, E_DOMAIN, 2, + "maximum number of attributes reached"); + return FALSE; + } + + attr_data.name = strdup (attr); + if (NULL == attr_data.name) + { + set_status (status, E_ALLOC, 3, "strdup failed"); + return FALSE; + } + + if (NULL == tag) + { + attr_data.tags = NULL; + attr_data.tag_n = 0; + } + else + { + attr_data.tags = malloc (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.tags[0].name = tag; + attr_data.tag_n = 1; + } + + /* allocate new space */ + /* The addition and the multiplication is save, + * because we have tested for this + * in the first condition. */ + new_attrs = realloc (validator->attrs, + sizeof (struct attr_info) + * (validator->attr_n + 1)); + + if (new_attrs == NULL) + { + set_status (status, E_ALLOC, 6, "realloc failed"); + +/* bad code to silence splint, should never be executed. */ +#ifdef S_SPLINT_S + validator->attrs = (void *) 0x12345; +#endif + free_attr (attr_data); + return FALSE; + } + + validator->attrs = new_attrs; + + for (size_t i = validator->attr_n; i > index; i--) + { + validator->attrs[i] = validator->attrs[i-1]; + } + + /* commit changes */ + validator->attrs[index] = attr_data; + validator->attr_n++; + + set_success (status); + return TRUE; +} + +static inline +bool +remove_attr (struct SH_Validator * validator, + const size_t index, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + struct attr_info attr_data; + struct attr_info * new_attrs; + + /* preserve data as realloc may fail */ + attr_data = validator->attrs[index]; + + for (size_t i = index; i < validator->attr_n-1; i++) + { + validator->attrs[i] = validator->attrs[i+1]; + } + + new_attrs = realloc (validator->attrs, + sizeof (struct attr_info) + * (validator->attr_n - 1)); + + if ((1 != validator->attr_n) && (NULL == new_attrs)) + { + set_status (status, E_ALLOC, 3, "realloc failed"); + + for (size_t i = validator->attr_n-1; i > index; i--) + { + validator->attrs[i] = validator->attrs[i-1]; + } + validator->attrs[index] = attr_data; + + return FALSE; + } + + free_attr (attr_data); + + validator->attrs = new_attrs; + validator->attr_n--; + + set_success (status); + return TRUE; +} + +static inline +bool +attr_add_tag (struct attr_info * attr, + const size_t index, + /*@null@*/ /*@dependent@*/ const char * tag, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies attr@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + struct attr_tag_info * new_tags; + + if (0 == attr->tag_n) + { + set_success (status); + return TRUE; + } + + if (NULL == tag) + { + free (attr->tags); + attr->tags = NULL; + attr->tag_n = 0; + + set_success (status); + return TRUE; + } + + if ((attr->tag_n == SIZE_MAX) + || ((attr->tag_n + 1) + > (SIZE_MAX / sizeof (struct attr_tag_info)))) + { + set_status (status, E_DOMAIN, 2, + "maximum number of tags per attr reached"); + return FALSE; + } + + new_tags = realloc (attr->tags, sizeof (struct attr_tag_info) + * (attr->tag_n + 1)); + if (NULL == new_tags) + { + set_status (status, E_ALLOC, 4, "realloc failed"); + return FALSE; + } + + for (size_t i = attr->tag_n; i > index; i--) + { + new_tags[i] = new_tags[i-1]; + } + new_tags[index].name = tag; + + attr->tags = new_tags; + attr->tag_n++; + + return TRUE; +} + +static inline +bool +attr_remove_tag (struct attr_info * attr, + const size_t index, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies attr@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + struct attr_tag_info tag_data; + struct attr_tag_info * new_tags; + + /* preserve data as realloc may fail */ + tag_data = attr->tags[index]; + + for (size_t i = index; i < attr->tag_n-1; i++) + { + attr->tags[i] = attr->tags[i+1]; + } + + new_tags = realloc (attr->tags, sizeof (struct attr_tag_info) + * (attr->tag_n - 1)); + if (NULL == new_tags) + { + set_status (status, E_ALLOC, 4, "realloc failed"); + + for (size_t i = attr->tag_n; i > index; i--) + { + attr->tags[i] = attr->tags[i-1]; + } + attr->tags[index] = tag_data; + + return FALSE; + } + + attr->tags = new_tags; + attr->tag_n--; + + set_success (status); + return TRUE; +} + +static inline +bool +remove_tag_for_all_attrs (struct SH_Validator * validator, + /*@dependent@*/ const char * tag, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + /* note, that index is used for two different arrays */ + size_t index; + + for (index = 0; index < validator->attr_n; index++) + { + struct attr_info * attr; + attr = &validator->attrs[index]; + + if (1 == attr->tag_n) + { + if (tag == attr->tags[0].name) + { + if (!remove_attr (validator, index, + status)) + { + return FALSE; + } + /* rescan current index, + * as the current attr just vanished */ + index--; + } + } + else + { + size_t index; + if (find_attr_tag (attr, tag, &index)) + { + if (!attr_remove_tag (attr, index, + status)) + { + return FALSE; + } + } + } + } + + set_success (status); + return TRUE; +} + +static inline +bool +attr_global_remove_tag (struct attr_info * attr, + const struct tag_info * tags, + const size_t tag_n, + const char * tag, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies attr->tags@*/ + /*@modifies attr->tag_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + attr->tags = malloc ((tag_n-1) * sizeof (struct attr_tag_info)); + if (NULL == attr->tags) + { + set_status (status, E_ALLOC, 3, "malloc failed"); + return FALSE; + } + + for (size_t index = 0; index < tag_n; index++) + { + size_t position; + + /* remove this tag */ + if (tag == tags[index].name) continue; + + find_attr_tag (attr, tags[index].name, &position); + + for (size_t i = attr->tag_n; i > position; i--) + { + attr->tags[i] = attr->tags[i-1]; + } + attr->tags[position].name = tags[index].name; + attr->tag_n++; + } + + set_success (status); + return TRUE; +} + +bool +SH_Validator_check_attr (const struct SH_Validator * validator, + /*@null@*/ const char * tag, + const char * attr) + /*@*/ +{ + /* note, that index is used for three different arrays */ + size_t index; + + if (!find_attr (validator, attr, &index)) return FALSE; + + { + struct attr_info * attr; + attr = &validator->attrs[index]; + + if (NULL == tag) return TRUE; + + if (!find_tag (validator, tag, &index)) return FALSE; + tag = validator->tags[index].name; + + /* global attr */ + if (0 == attr->tag_n) return TRUE; + + if (!find_attr_tag (attr, tag, &index)) return FALSE; + } + + return TRUE; +} + +bool +SH_Validator_register_attr (struct SH_Validator * validator, + /*@null@*/ const char * tag, + const char * attr, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + /* note, that index is used for three different arrays */ + size_t index; + + if (NULL != tag) + { + if (!find_tag (validator, tag, &index)) + { + set_status (status, E_STATE, 2, "no such tag"); + return FALSE; + } + + tag = validator->tags[index].name; + } + + /* attr already registered */ + if (find_attr (validator, attr, &index)) + { + struct attr_info * attr; + attr = &validator->attrs[index]; + + if (NULL == tag) + { + return attr_add_tag (attr, 0, NULL, status); + } + + if ((!find_attr_tag (attr, tag, &index)) + && (!attr_add_tag (attr, index, tag, status))) + { + return FALSE; + } + + set_success (status); + return TRUE; + } + + return add_attr (validator, attr, tag, index, status); +} + +bool +SH_Validator_deregister_attr (struct SH_Validator * validator, + /*@null@*/ const char * tag, + /*@null@*/ const char * attr, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@modifies validator->attr_n@*/ + /*@modifies validator->attrs@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + /* note, that index is used for three different arrays */ + size_t index; + + if ((NULL == tag) && (NULL == attr)) + { + free_attrs (validator); + init_attrs (validator); + set_success (status); + return TRUE; + } + + if (NULL == attr) + { + if (!find_tag (validator, tag, &index)) + { + /* TODO: define whether this is an error */ + set_status (status, E_VALUE, 3, "no such tag"); + return FALSE; + } + + tag = validator->tags[index].name; + return remove_tag_for_all_attrs (validator, tag, status); + } + + if (!find_attr (validator, attr, &index)) + { + /* TODO: define whether this is an error */ + set_status (status, E_VALUE, 3, "no such attr"); + return FALSE; + } + + { + struct attr_info * attr; + attr = &validator->attrs[index]; + + if (NULL == tag) + { + return remove_attr (validator, index, status); + } + + if (1 == attr->tag_n) + { + /* TODO: define whether this is an error */ + if (0 != strcmp (tag, attr->tags[0].name)) + { + set_status (status, E_VALUE, 2, + "no such tag"); + return FALSE; + } + + return remove_attr (validator, index, status); + } + + if ((0 == attr->tag_n) && (1 == validator->tag_n)) + { + /* TODO: define whether this is an error */ + if (0 != strcmp (tag, validator->tags[0].name)) + { + set_status (status, E_VALUE, 2, + "no such tag"); + return FALSE; + } + + return remove_attr (validator, index, status); + } + + if (!find_tag (validator, tag, &index)) + { + /* TODO: define whether this is an error */ + set_status (status, E_VALUE, 3, "no such tag"); + return FALSE; + } + + tag = validator->tags[index].name; + + if (0 == attr->tag_n) + { + return attr_global_remove_tag (attr, + validator->tags, + validator->tag_n, + tag, status); + } + + if (!find_attr_tag (attr, tag, &index)) + { + /* TODO: define whether this is an error */ + set_status (status, E_VALUE, 3, "no such tag"); + return FALSE; + } + + return attr_remove_tag (attr, index, status); + } +} diff --git a/src/lib/sefht/validator_attr.h b/src/lib/sefht/validator_attr.h new file mode 100644 index 0000000000000000000000000000000000000000..c8745e806fa910e7061d4a73bcfb3c19800462ee --- /dev/null +++ b/src/lib/sefht/validator_attr.h @@ -0,0 +1,66 @@ +/* + * validator_attr.h + * + * Copyright 2023 Jonathan Schöbel <jonathan@xn--schbel-yxa.info> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + */ + + +#ifndef SEFHT_VALIDATOR_ATTR_H +#define SEFHT_VALIDATOR_ATTR_H + +#if !defined (SEFHT_VALIDATOR_H) +#error "Please include only <sefht/validator.h>." +#endif + +#include <stdbool.h> +#include <stdint.h> + +#include "status.h" + + +bool +SH_Validator_register_attr (SH_Validator * validator, + const char * tag, + const char * attr, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@modifies validator@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + +bool +SH_Validator_deregister_attr (SH_Validator * validator, + /*@null@*/ const char * tag, + /*@null@*/ const char * attr, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@modifies validator@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + +bool +SH_Validator_check_attr (const SH_Validator * validator, + /*@null@*/ const char * tag, + const char * attr) + /*@*/; + +#endif /* SEFHT_VALIDATOR_ATTR_H */ diff --git a/src/lib/sefht/validator_attr_data.h b/src/lib/sefht/validator_attr_data.h new file mode 100644 index 0000000000000000000000000000000000000000..b877f117a0cbc00e89d5bdc5f620b41ec1f82cf3 --- /dev/null +++ b/src/lib/sefht/validator_attr_data.h @@ -0,0 +1,48 @@ +/* + * validator_attr_data.h + * + * Copyright 2023 Jonathan Schöbel <jonathan@xn--schbel-yxa.info> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + */ + + +#ifndef SEFHT_VALIDATOR_ATTR_DATA_H +#define SEFHT_VALIDATOR_ATTR_DATA_H + +#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION) +#error "Only <sefht/sefht.h> can be included directly." +#endif + +struct attr_tag_info +{ + /*@relnull@*/ /*@dependent@*/ const char * name; +}; + +struct attr_info +{ + /*@only@*/ char * name; + size_t tag_n; + struct attr_tag_info * tags; +}; + +#define ATTR_DATA \ + /*@only@*/ struct attr_info * attrs; \ + size_t attr_n; \ + +#endif /* SEFHT_VALIDATOR_ATTR_DATA_H */ diff --git a/src/lib/sefht/validator_html.h b/src/lib/sefht/validator_html.h new file mode 100644 index 0000000000000000000000000000000000000000..9e419a2cba7c3bbb27df9f78efbae309892a87e3 --- /dev/null +++ b/src/lib/sefht/validator_html.h @@ -0,0 +1,167 @@ +/* + * validator_html.h + * + * Copyright 2023 Jonathan Schöbel <jonathan@xn--schbel-yxa.info> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + */ + + +#ifndef SEFHT_VALIDATOR_HTML_H +#define SEFHT_VALIDATOR_HTML_H + +#if !defined (SEFHT_COMPILATION) +#error "Only <sefht/sefht.h> can be included directly." +#endif + +#include "validator.h" + + +struct HTML_TAG_DEFINITION +{ + /*@observer@*/ const char * tag; + enum SH_VALIDATOR_TAG_TYPE type; +}; + +struct HTML_ATTR_DEFINITION +{ + /*@observer@*/ const char * attr; + /*@observer@*/ const struct HTML_TAG_DEFINITION * tags; + size_t tag_n; +}; + + +static const struct HTML_TAG_DEFINITION HTML5_TAGS[] = { + {"a", SH_TAG_TYPE_NORMAL}, + {"abbr", SH_TAG_TYPE_NORMAL}, + {"address", SH_TAG_TYPE_NORMAL}, + {"area", SH_TAG_TYPE_VOID}, + {"article", SH_TAG_TYPE_NORMAL}, + {"aside", SH_TAG_TYPE_NORMAL}, + {"audio", SH_TAG_TYPE_NORMAL}, + {"b", SH_TAG_TYPE_NORMAL}, + {"base", SH_TAG_TYPE_VOID}, + {"bdi", SH_TAG_TYPE_NORMAL}, + {"bdo", SH_TAG_TYPE_NORMAL}, + {"blockquote", SH_TAG_TYPE_NORMAL}, + {"body", SH_TAG_TYPE_NORMAL}, + {"br", SH_TAG_TYPE_VOID}, + {"button", SH_TAG_TYPE_NORMAL}, + {"canvas", SH_TAG_TYPE_NORMAL}, + {"caption", SH_TAG_TYPE_NORMAL}, + {"cite", SH_TAG_TYPE_NORMAL}, + {"code", SH_TAG_TYPE_NORMAL}, + {"col", SH_TAG_TYPE_VOID}, + {"colgroup", SH_TAG_TYPE_NORMAL}, + {"data", SH_TAG_TYPE_NORMAL}, + {"datalist", SH_TAG_TYPE_NORMAL}, + {"dd", SH_TAG_TYPE_NORMAL}, + {"del", SH_TAG_TYPE_NORMAL}, + {"details", SH_TAG_TYPE_NORMAL}, + {"dfn", SH_TAG_TYPE_NORMAL}, + {"dialog", SH_TAG_TYPE_NORMAL}, + {"div", SH_TAG_TYPE_NORMAL}, + {"dl", SH_TAG_TYPE_NORMAL}, + {"dt", SH_TAG_TYPE_NORMAL}, + {"em", SH_TAG_TYPE_NORMAL}, + {"embed", SH_TAG_TYPE_VOID}, + {"fieldset", SH_TAG_TYPE_NORMAL}, + {"figcaption", SH_TAG_TYPE_NORMAL}, + {"figure", SH_TAG_TYPE_NORMAL}, + {"footer", SH_TAG_TYPE_NORMAL}, + {"form", SH_TAG_TYPE_NORMAL}, + {"h1", SH_TAG_TYPE_NORMAL}, + {"h2", SH_TAG_TYPE_NORMAL}, + {"h3", SH_TAG_TYPE_NORMAL}, + {"h4", SH_TAG_TYPE_NORMAL}, + {"h5", SH_TAG_TYPE_NORMAL}, + {"h6", SH_TAG_TYPE_NORMAL}, + {"head", SH_TAG_TYPE_NORMAL}, + {"header", SH_TAG_TYPE_NORMAL}, + {"hgroup", SH_TAG_TYPE_NORMAL}, + {"hr", SH_TAG_TYPE_VOID}, + {"html", SH_TAG_TYPE_NORMAL}, + {"i", SH_TAG_TYPE_NORMAL}, + {"iframe", SH_TAG_TYPE_NORMAL}, + {"img", SH_TAG_TYPE_VOID}, + {"input", SH_TAG_TYPE_VOID}, + {"ins", SH_TAG_TYPE_NORMAL}, + {"kbd", SH_TAG_TYPE_NORMAL}, + {"label", SH_TAG_TYPE_NORMAL}, + {"legend", SH_TAG_TYPE_NORMAL}, + {"li", SH_TAG_TYPE_NORMAL}, + {"link", SH_TAG_TYPE_VOID}, + {"main", SH_TAG_TYPE_NORMAL}, + {"map", SH_TAG_TYPE_NORMAL}, + {"mark", SH_TAG_TYPE_NORMAL}, + {"menu", SH_TAG_TYPE_NORMAL}, + {"meta", SH_TAG_TYPE_VOID}, + {"meter", SH_TAG_TYPE_NORMAL}, + {"nav", SH_TAG_TYPE_NORMAL}, + {"noscript", SH_TAG_TYPE_NORMAL}, + {"object", SH_TAG_TYPE_NORMAL}, + {"ol", SH_TAG_TYPE_NORMAL}, + {"optgroup", SH_TAG_TYPE_NORMAL}, + {"option", SH_TAG_TYPE_NORMAL}, + {"output", SH_TAG_TYPE_NORMAL}, + {"p", SH_TAG_TYPE_NORMAL}, + {"picture", SH_TAG_TYPE_NORMAL}, + {"pre", SH_TAG_TYPE_NORMAL}, + {"progress", SH_TAG_TYPE_NORMAL}, + {"q", SH_TAG_TYPE_NORMAL}, + {"rp", SH_TAG_TYPE_NORMAL}, + {"rt", SH_TAG_TYPE_NORMAL}, + {"ruby", SH_TAG_TYPE_NORMAL}, + {"s", SH_TAG_TYPE_NORMAL}, + {"samp", SH_TAG_TYPE_NORMAL}, + {"script", SH_TAG_TYPE_RAW_TEXT}, + {"search", SH_TAG_TYPE_NORMAL}, + {"section", SH_TAG_TYPE_NORMAL}, + {"select", SH_TAG_TYPE_NORMAL}, + {"slot", SH_TAG_TYPE_NORMAL}, + {"small", SH_TAG_TYPE_NORMAL}, + {"source", SH_TAG_TYPE_VOID}, + {"span", SH_TAG_TYPE_NORMAL}, + {"strong", SH_TAG_TYPE_NORMAL}, + {"style", SH_TAG_TYPE_RAW_TEXT}, + {"sub", SH_TAG_TYPE_NORMAL}, + {"summary", SH_TAG_TYPE_NORMAL}, + {"sup", SH_TAG_TYPE_NORMAL}, + {"table", SH_TAG_TYPE_NORMAL}, + {"tbody", SH_TAG_TYPE_NORMAL}, + {"td", SH_TAG_TYPE_NORMAL}, + {"template", SH_TAG_TYPE_TEMPLATE}, + {"textarea", SH_TAG_TYPE_ESC_RAW_TEXT}, + {"tfoot", SH_TAG_TYPE_NORMAL}, + {"th", SH_TAG_TYPE_NORMAL}, + {"thead", SH_TAG_TYPE_NORMAL}, + {"time", SH_TAG_TYPE_NORMAL}, + {"title", SH_TAG_TYPE_ESC_RAW_TEXT}, + {"tr", SH_TAG_TYPE_NORMAL}, + {"track", SH_TAG_TYPE_VOID}, + {"u", SH_TAG_TYPE_NORMAL}, + {"ul", SH_TAG_TYPE_NORMAL}, + {"var", SH_TAG_TYPE_NORMAL}, + {"video", SH_TAG_TYPE_NORMAL}, + {"wbr", SH_TAG_TYPE_VOID}, +}; + +static const struct HTML_ATTR_DEFINITION HTML5_ATTRS[] = { +}; + +#endif /* SEFHT_VALIDATOR_HTML_H */ diff --git a/src/lib/sefht/validator_tag.c b/src/lib/sefht/validator_tag.c index 076eec0a0f49647f48d5e92e4b3c4f89630d9f69..a0f51c2662d3bfa826f006e4d00d6bb15d47f12a 100644 --- a/src/lib/sefht/validator_tag.c +++ b/src/lib/sefht/validator_tag.c @@ -33,41 +33,51 @@ #include "validator_tag.h" +#include "validator_html.h" -#ifndef VALIDATOR_IS_DEFINED -#define NEXT_TAG(tag) (tag + 1) +static inline +bool +tag_type_to_internal (/*@out@*/ enum TAG_TYPE * internal, + enum SH_VALIDATOR_TAG_TYPE external) + /*@modifies internal@*/; -struct tag_info -{ - union - { - struct - { - Tag id; - /*@only@*/ char * name; - } data; - size_t next; - }; -}; +/*@unused@*/ +static inline +enum SH_VALIDATOR_TAG_TYPE +tag_type_to_external (enum TAG_TYPE type) + /*@*/; -#define TAG_DATA \ - /*@only@*/ struct tag_info * tags; \ - size_t tag_n; \ - Tag last_tag; \ +static inline +void +init_tags (/*@special@*/ struct SH_Validator * validator) + /*@defines validator->tags, + validator->tag_n@*/ + /*@modifies validator->tags@*/ + /*@modifies validator->tag_n@*/; -#else /* VALIDATOR_IS_DEFINED */ +static inline +bool +init_tags_from_spec (/*@special@*/ struct SH_Validator * validator, + const struct HTML_TAG_DEFINITION * tags, + const size_t size, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@defines validator->tags, + validator->tag_n@*/ + /*@modifies validator->tags@*/ + /*@modifies validator->tag_n@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; static inline bool -init_tags (/*@special@*/ struct SH_Validator * validator, - /*@null@*/ /*@out@*/ struct SH_Status * status) +init_tags_spec_html5 (/*@special@*/ struct SH_Validator * validator, + /*@null@*/ /*@out@*/ struct SH_Status * status) /*@defines validator->tags, - validator->tag_n, - validator->last_tag@*/ + validator->tag_n@*/ /*@modifies validator->tags@*/ /*@modifies validator->tag_n@*/ - /*@modifies validator->last_tag@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ /*@modifies status@*/; @@ -78,15 +88,19 @@ copy_tags (/*@special@*/ struct SH_Validator * copy, const struct SH_Validator * validator, /*@null@*/ /*@out@*/ struct SH_Status * status) /*@defines copy->tags, - copy->tag_n, - copy->last_tag@*/ + copy->tag_n@*/ /*@modifies copy->tags@*/ /*@modifies copy->tag_n@*/ - /*@modifies copy->last_tag@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ /*@modifies status@*/; +static inline +void +free_tag (struct tag_info tag) + /*@modifies tag.name@*/ + /*@releases tag.name@*/; + static inline void free_tags (/*@special@*/ struct SH_Validator * validator) @@ -94,570 +108,559 @@ free_tags (/*@special@*/ struct SH_Validator * validator) /*@releases validator->tags@*/; static inline -size_t -get_tag_number (const struct SH_Validator * validator) - /*@*/; +bool +find_tag (const struct SH_Validator * validator, + const char * tag, + /*@out@*/ size_t * index) + /*@modifies index@*/; static inline -Tag +bool add_tag (struct SH_Validator * validator, const char * tag, + enum TAG_TYPE type, + size_t index, /*@null@*/ /*@out@*/ struct SH_Status * status) /*@modifies validator->tags@*/ /*@modifies validator->tag_n@*/ - /*@modifies validator->last_tag@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ /*@modifies status@*/; -/*@unused@*/ static inline bool -is_tag_id (const struct SH_Validator * validator, Tag id) - /*@*/; +remove_tag (struct SH_Validator * validator, + const size_t index, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@modifies validator->tag_n@*/ + /*@modifies validator->tags@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + static inline bool -is_tag_name (const struct SH_Validator * validator, const char * name) - /*@*/; +tag_type_to_internal (/*@out@*/ enum TAG_TYPE * internal, + enum SH_VALIDATOR_TAG_TYPE external) + /*@modifies internal@*/ +{ + switch (external) + { + case SH_TAG_TYPE_VOID: + *internal = TAG_T_VOID; + return TRUE; + + case SH_TAG_TYPE_TEMPLATE: + *internal = TAG_T_TEMPLATE; + return TRUE; + + case SH_TAG_TYPE_RAW_TEXT: + *internal = TAG_T_RAW_TEXT; + return TRUE; + + case SH_TAG_TYPE_ESC_RAW_TEXT: + *internal = TAG_T_ESC_RAW_TEXT; + return TRUE; + + case SH_TAG_TYPE_FOREIGN: + *internal = TAG_T_FOREIGN; + return TRUE; + + case SH_TAG_TYPE_NORMAL: + *internal = TAG_T_NORMAL; + return TRUE; + + default: + return FALSE; + } +} /*@unused@*/ static inline -/*@null@*/ -/*@only@*/ -char * -get_tag_name_by_id (const struct SH_Validator * validator, Tag id, - /*@null@*/ /*@out@*/ struct SH_Status * status) - /*@globals fileSystem@*/ - /*@modifies fileSystem@*/ - /*@modifies status@*/; +enum SH_VALIDATOR_TAG_TYPE +tag_type_to_external (enum TAG_TYPE type) + /*@*/ +{ + switch (type) + { + case TAG_T_VOID: return SH_TAG_TYPE_VOID; + case TAG_T_TEMPLATE: return SH_TAG_TYPE_TEMPLATE; + case TAG_T_RAW_TEXT: return SH_TAG_TYPE_RAW_TEXT; + case TAG_T_ESC_RAW_TEXT: return SH_TAG_TYPE_ESC_RAW_TEXT; + case TAG_T_FOREIGN: return SH_TAG_TYPE_FOREIGN; + case TAG_T_NORMAL: return SH_TAG_TYPE_NORMAL; + } +} static inline -Tag -get_tag_id_by_name (const struct SH_Validator * validator, - const char * name) - /*@*/; +void +init_tags (/*@special@*/ struct SH_Validator * validator) + /*@defines validator->tags, + validator->tag_n@*/ + /*@modifies validator->tags@*/ + /*@modifies validator->tag_n@*/ +{ + validator->tags = malloc (0); + validator->tag_n = 0; + return; +} static inline bool -remove_tag (struct SH_Validator * validator, Tag id, - /*@null@*/ /*@out@*/ struct SH_Status * status) - /*@modifies validator->tag_n@*/ +init_tags_spec_html5 (/*@special@*/ struct SH_Validator * validator, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@defines validator->tags, + validator->tag_n@*/ /*@modifies validator->tags@*/ - /*@modifies validator->last_tag@*/ + /*@modifies validator->tag_n@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ - /*@modifies status@*/; - - -#define EXECUTE_ON_ALL_TAGS_IF(ITER, CONDITION, BLOCK) \ -do \ -{ \ - bool is_free; \ - size_t index; \ - size_t free_index; \ - \ - for (index = (size_t) 1; index <= ITER##_n; index++) \ - { \ - /* if tag is not in the list of free blocks */ \ - is_free = FALSE; \ - for (free_index = ITER##s[0].next; \ - free_index != 0; \ - free_index = ITER##s[free_index].next) \ - { \ - if (index == free_index) \ - { \ - is_free = TRUE; \ - \ - /*@innerbreak@*/ \ - break; \ - } \ - } \ - \ - if (!is_free && CONDITION) BLOCK \ - } \ -} \ -while (FALSE) - -#define EXECUTE_ON_ALL_TAGS(BASE, BLOCK) \ - EXECUTE_ON_ALL_TAGS_IF (BASE, TRUE, BLOCK) - + /*@modifies status@*/ +{ + size_t size; + size = sizeof (HTML5_TAGS) / sizeof (HTML5_TAGS[0]); + return init_tags_from_spec (validator, HTML5_TAGS, size, status); +} static inline bool -init_tags (/*@special@*/ struct SH_Validator * validator, - /*@null@*/ /*@out@*/ struct SH_Status * status) +init_tags_from_spec (/*@special@*/ struct SH_Validator * validator, + const struct HTML_TAG_DEFINITION * spec, + const size_t size, + /*@null@*/ /*@out@*/ struct SH_Status * status) /*@defines validator->tags, - validator->tag_n, - validator->last_tag@*/ + validator->tag_n@*/ /*@modifies validator->tags@*/ /*@modifies validator->tag_n@*/ - /*@modifies validator->last_tag@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ /*@modifies status@*/ { - validator->tags = malloc (sizeof (struct tag_info)); - if (validator->tags == NULL) + size_t index; + + /* overflow detection */ + if ((size >= (SIZE_MAX / sizeof (struct tag_info)))) { - set_status (status, E_ALLOC, 3, "malloc failed"); + set_status (status, E_DOMAIN, 2, + "maximum number of tags reached"); + return FALSE; + } - validator->tag_n = 0; - validator->last_tag = TAG_ERR; + validator->tags = malloc (sizeof (struct tag_info) * size); + if (NULL == validator->tags) + { + set_status (status, E_ALLOC, 3, "malloc failed"); return FALSE; } - validator->tags[0].next = 0; validator->tag_n = 0; - validator->last_tag = TAG_ERR; + /* insertion sort */ + #define tags validator->tags + #define tag_n validator->tag_n + for (index = 0; index < size; index++) + { + size_t position; + + /* ignore duplicate tags */ + if (!find_tag (validator, spec[index].tag, &position)) + { + struct tag_info tag_data; + + if (!tag_type_to_internal (&tag_data.type, + spec[index].type)) + { + set_status (status, E_VALUE, 3, + "invalid tag type"); + free_tags (validator); + return FALSE; + } + + tag_data.name = strdup (spec[index].tag); + if (NULL == tag_data.name) + { + set_status (status, E_ALLOC, 3, + "strdup failed"); + free_tags (validator); + return FALSE; + } + + for (size_t i = tag_n; i > position; i--) + { + tags[i] = tags[i-1]; + } + + tags[position] = tag_data; + tag_n++; + } + } + #undef tag_n + #undef tags + + set_success (status); return TRUE; } +static inline +bool +copy_tag (/*@special@*/ struct tag_info * copy, + const struct tag_info * tag, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@defines copy->name@*/ + /*@modifies copy->name@*/ + /*@defines copy->type@*/ + /*@modifies copy->type@*/ + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + char * name; + + name = strdup (tag->name); + if (NULL == name) + { + set_status (status, E_ALLOC, 3, "strdup failed"); + return FALSE; + } + + copy->name = name; + copy->type = tag->type; + return TRUE; + +} static inline bool copy_tags (/*@special@*/ struct SH_Validator * copy, const struct SH_Validator * validator, /*@null@*/ /*@out@*/ struct SH_Status * status) /*@defines copy->tags, - copy->tag_n, - copy->last_tag@*/ + copy->tag_n@*/ /*@modifies copy->tags@*/ /*@modifies copy->tag_n@*/ - /*@modifies copy->last_tag@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ /*@modifies status@*/ { - bool is_free; size_t index; - size_t free_index; - size_t copy_index; - size_t tag_n; - - tag_n = get_tag_number (validator); /* The size calculation is save, * because validator is already allocated. */ - copy->tags = malloc ((tag_n + 1) * sizeof (struct tag_info)); + copy->tags = malloc (validator->tag_n * sizeof (struct tag_info)); if (copy->tags == NULL) { set_status (status, E_ALLOC, 5, "malloc failed"); - - copy->tag_n = 0; - copy->last_tag = TAG_ERR; return FALSE; } - /* copy allocation info */ - copy->tags[0].next = 0; - copy->tag_n = tag_n; - copy->last_tag = validator->last_tag; - - /* copy data */ - copy_index = 0; - for (index = (size_t) 1; index <= validator->tag_n; index++) + for (index = 0; index < validator->tag_n; index++) { - /* if tag is not in the list of free blocks */ - is_free = FALSE; - for (free_index = validator->tags[0].next; - free_index != 0; - free_index = validator->tags[free_index].next) - { - if (index == free_index) - { - is_free = TRUE; - - /*@innerbreak@*/ - break; - } - } - - if (!is_free) + if (!copy_tag (©->tags[index], + &validator->tags[index], status)) { - copy->tags[copy_index].data.id = - validator->tags[index].data.id; - - copy->tags[copy_index].data.name = strdup ( - validator->tags[index].data.name); - - if (copy->tags[copy_index].data.name == NULL) - { - size_t index; - - set_status (status, E_ALLOC, 5, - "strdup failed"); - - for (index = 0; index < copy_index; - index++) - { - free (copy->tags[index] - .data.name); - } - - free (copy->tags); - - return FALSE; - } - - copy_index++; + copy->tag_n = index; + free_tags (copy); + return FALSE; } } + copy->tag_n = validator->tag_n; + return TRUE; } +static inline +void +free_tag (struct tag_info tag) + /*@modifies tag.name@*/ + /*@releases tag.name@*/ +{ + free (tag.name); + return; +} + static inline void free_tags (/*@special@*/ struct SH_Validator * validator) /*@modifies validator->tags@*/ /*@releases validator->tags@*/ { - EXECUTE_ON_ALL_TAGS ( - validator->tag, + size_t index; + + for (index = 0; index < validator->tag_n; index++) { - free (validator->tags[index].data.name); - }); + free_tag (validator->tags[index]); + } free (validator->tags); return; } static inline -size_t -get_tag_number (const struct SH_Validator * validator) - /*@*/ +bool +find_tag (const struct SH_Validator * validator, + const char * tag, + /*@out@*/ size_t * index) + /*@modifies index@*/ { - size_t tag_n; + size_t start; + size_t end; + size_t pivot; - tag_n = 0; + start = 0; + end = validator->tag_n; + pivot = (end - start) / 2; - EXECUTE_ON_ALL_TAGS ( - validator->tag, + while (start != end) { - /* This addition is always save, - * because tag_n is always smaller than - * validator->tag_n and it is also of size_t. */ - tag_n++; - }); + int cmp = strcmp (tag, validator->tags[pivot].name); + + if (0 > cmp) + { + end = pivot; + pivot = (pivot - start) / 2 + start; + } + else if (0 == cmp) + { + *index = pivot; + return TRUE; + } + else + { + start = pivot + 1; + pivot = (end - pivot) / 2 + pivot; + } + } - return tag_n; + *index = start; + return FALSE; } static inline -Tag +bool add_tag (struct SH_Validator * validator, const char * tag, + enum TAG_TYPE type, + size_t index, /*@null@*/ /*@out@*/ struct SH_Status * status) /*@modifies validator->tags@*/ /*@modifies validator->tag_n@*/ - /*@modifies validator->last_tag@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ /*@modifies status@*/ { - Tag tag_id; - size_t index; - bool is_new; + struct tag_info tag_data; + struct tag_info * new_tags; /* abort on overflow: - * - no unused Tag or * - no unused index */ - if ((validator->last_tag == TAG_MAX) - || ((validator->tags[0].next == 0) - && ((validator->tag_n >= SIZE_MAX - 1) - || ((validator->tag_n + 2) - > (SIZE_MAX / sizeof (struct tag_info)))))) + if ((validator->tag_n == SIZE_MAX) + || ((validator->tag_n + 1) + > (SIZE_MAX / sizeof (struct tag_info)))) { set_status (status, E_DOMAIN, 2, "maximum number of tags reached"); - return TAG_ERR; + return FALSE; } - if (validator->tags[0].next == 0) - /* allocate new space */ + tag_data.name = strdup (tag); + if (NULL == tag_data.name) { - struct tag_info * new_tags; + set_status (status, E_ALLOC, 3, "strdup failed"); + return FALSE; + } - /* The addition and the multiplication is save, - * because we have tested for this - * in the first condition. */ - new_tags = realloc (validator->tags, - sizeof (struct tag_info) - * (validator->tag_n + 2)); + tag_data.type = type; - if (new_tags == NULL) - { - set_status (status, E_ALLOC, 6, - "realloc failed"); + /* allocate new space */ + /* The addition and the multiplication is save, + * because we have tested for this + * in the first condition. */ + new_tags = realloc (validator->tags, + sizeof (struct tag_info) + * (validator->tag_n + 1)); + + if (NULL == new_tags) + { + set_status (status, E_ALLOC, 6, "realloc failed"); /* bad code to silence splint, should never be executed. */ #ifdef S_SPLINT_S - validator->tags = (void *) 0x12345; + validator->tags = (void *) 0x12345; #endif - return TAG_ERR; - } - - validator->tags = new_tags; - index = validator->tag_n + 1; - is_new = TRUE; - } - /* reuse old space */ - else - { - index = validator->tags[0].next; - validator->tags[0].next = validator->tags[index].next; - is_new = FALSE; + return FALSE; } - tag_id = NEXT_TAG (validator->last_tag); - validator->tags[index].data.id = tag_id; - validator->tags[index].data.name = strdup (tag); + validator->tags = new_tags; - if (validator->tags[index].data.name == NULL) + for (size_t i = validator->tag_n; i > index; i--) { - set_status (status, E_ALLOC, 4, "strdup failed"); - - /* restore free space list */ - if (!is_new) - { - validator->tags[index].next = \ - validator->tags[0].next; - validator->tags[0].next = index; - } - - return TAG_ERR; + validator->tags[i] = validator->tags[i-1]; } /* commit changes */ - if (is_new) - { - validator->tag_n++; - } - - validator->last_tag = tag_id; + validator->tags[index] = tag_data; + validator->tag_n++; set_success (status); - - return tag_id; -} - -/*@unused@*/ -static inline -bool -is_tag_id (const struct SH_Validator * validator, Tag id) - /*@*/ -{ - EXECUTE_ON_ALL_TAGS_IF ( - validator->tag, - (validator->tags[index].data.id == id), - { - return TRUE; - }); - - return FALSE; + return TRUE; } static inline bool -is_tag_name (const struct SH_Validator * validator, const char * name) - /*@*/ -{ - EXECUTE_ON_ALL_TAGS_IF ( - validator->tag, - (strcmp (validator->tags[index].data.name, name) == 0), - { - return TRUE; - }); - - return FALSE; -} - -/*@unused@*/ -static inline -/*@null@*/ -/*@only@*/ -char * -get_tag_name_by_id (const struct SH_Validator * validator, Tag id, - /*@null@*/ /*@out@*/ struct SH_Status * status) +remove_tag_for_all_attrs (struct SH_Validator * validator, + /*@dependent@*/ const char * tag, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@modifies validator->attrs@*/ + /*@modifies validator->attr_n@*/ + /*@modifies validator->last_attr@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ - /*@modifies status@*/ -{ - char * name; - - EXECUTE_ON_ALL_TAGS_IF ( - validator->tag, - (validator->tags[index].data.id == id), - { - name = strdup (validator->tags[index].data.name); - if (name == NULL) - { - set_status (status, E_ALLOC, 3, - "strdup failed"); - return NULL; - } - - return name; - }); - - return NULL; -} - -static inline -Tag -get_tag_id_by_name (const struct SH_Validator * validator, - const char * name) - /*@*/ -{ - EXECUTE_ON_ALL_TAGS_IF ( - validator->tag, - (strcmp (validator->tags[index].data.name, name) == 0), - { - return validator->tags[index].data.id; - }); - - return TAG_ERR; -} + /*@modifies status@*/; static inline bool -remove_tag (struct SH_Validator * validator, Tag id, +remove_tag (struct SH_Validator * validator, + const size_t index, /*@null@*/ /*@out@*/ struct SH_Status * status) /*@modifies validator->tag_n@*/ /*@modifies validator->tags@*/ - /*@modifies validator->last_tag@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ /*@modifies status@*/ { - #define tags validator->tags - #define tag_n validator->tag_n - EXECUTE_ON_ALL_TAGS_IF ( - tag, - (tags[index].data.id == id), - { - free (tags[index].data.name); + struct tag_info tag; + struct tag_info * new_tags; - if (index == tag_n) - { - struct tag_info * new_tags; + /* preserve data, as realloc may fail */ + tag = validator->tags[index]; - do - { - /* find next last free blocks - * in the list of free blocks */ - is_free = FALSE; - for (free_index = tags[0].next; - free_index != 0; - free_index = tags[free_index].next) - { - - if (tags[free_index].next == (index - 1)) - { - is_free = TRUE; - index--; - - tags[free_index].next = - tags[tags[free_index].next].next; - - /*@innerbreak@*/ - break; - } - } - } - while (is_free); - - if (index - > (SIZE_MAX / sizeof (struct tag_info))) - { - set_status (status, E_DOMAIN, 2, - "overflow while calling realloc"); - } + /* clear all references of this tag */ + if (!remove_tag_for_all_attrs (validator, tag.name, status)) + { + return FALSE; + } - new_tags = realloc (tags, - sizeof (struct tag_info) - * index); + for (size_t i = index; i < validator->tag_n-1; i++) + { + validator->tags[i] = validator->tags[i+1]; + } - if (new_tags == NULL) - { - set_status (status, E_ALLOC, 4, - "realloc failed"); + new_tags = realloc (validator->tags, sizeof (struct tag_info) + * (validator->tag_n - 1)); + if ((1 != validator->tag_n) && (NULL == new_tags)) + { + set_status (status, E_ALLOC, 3, "realloc failed"); + for (size_t i = validator->tag_n-1; i > index; i++) + { + validator->tags[i] = validator->tags[i-1]; + } + validator->tags[index] = tag; /* bad code to silence splint, should never be executed. */ #ifdef S_SPLINT_S - tags = (void *) 0x12345; + new_tags = (void *) 0x12345; #endif - return FALSE; - } - tags = new_tags; - } - else - { - tags[index].next = tags[0].next; - tags[0].next = index; - } + return FALSE; + } - set_success (status); - return TRUE; - }); - #undef tags - #undef tag_n + free_tag (tag); - set_status (status, E_VALUE, 68, "unknown Tag id"); - return FALSE; + validator->tags = new_tags; + validator->tag_n--; + + set_success (status); + return TRUE; } bool -SH_Validator_check_tag (struct SH_Validator * validator, +SH_Validator_check_tag (const struct SH_Validator * validator, const char * tag) /*@*/ { - return is_tag_name (validator, tag); + size_t index; + return find_tag (validator, tag, &index); +} + +bool +SH_Validator_is_self_closing_tag (const struct SH_Validator * validator, + const char * tag, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + size_t index; + + if (!find_tag (validator, tag, &index)) + { + set_status (status, E_VALUE, 2, "no such tag"); + return FALSE; + } + + set_success (status); + return (TAG_T_VOID == validator->tags[index].type); } -Tag +bool /*@alt void@*/ SH_Validator_register_tag (struct SH_Validator * validator, const char * tag, + enum SH_VALIDATOR_TAG_TYPE type, /*@null@*/ /*@out@*/ struct SH_Status * status) /*@modifies validator->tags@*/ /*@modifies validator->tag_n@*/ - /*@modifies validator->last_tag@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ /*@modifies status@*/ { - Tag tag_id; + size_t index; + enum TAG_TYPE type_internal; + + if (!tag_type_to_internal (&type_internal, type)) + { + set_status (status, E_VALUE, 2, "invalid tag type"); + return FALSE; + } /* tag already registered */ - tag_id = get_tag_id_by_name (validator, tag); - if (tag_id != TAG_ERR) + if (find_tag (validator, tag, &index)) { - return tag_id; + /* update type */ + validator->tags[index].type = type_internal; + set_success (status); + return TRUE; } - return add_tag (validator, tag, status); + return add_tag (validator, tag, type_internal, index, status); } bool SH_Validator_deregister_tag (struct SH_Validator * validator, - Tag id, + const char * tag, /*@null@*/ /*@out@*/ struct SH_Status * status) /*@modifies validator->tag_n@*/ /*@modifies validator->tags@*/ - /*@modifies validator->last_tag@*/ /*@globals fileSystem@*/ /*@modifies fileSystem@*/ /*@modifies status@*/ { - return remove_tag (validator, id, status); -} + size_t index; -#endif /* VALIDATOR_IS_DEFINED */ + if (!find_tag (validator, tag, &index)) + { + /* TODO: define whether this is an error */ + set_status (status, E_VALUE, 3, "no such tag"); + return FALSE; + } + + return remove_tag (validator, index, status); +} diff --git a/src/lib/sefht/validator_tag.h b/src/lib/sefht/validator_tag.h index a941d0b16b51556adc7858ce26e55971c3456d10..95d438528904ae97e711f9960436ce98080f8b16 100644 --- a/src/lib/sefht/validator_tag.h +++ b/src/lib/sefht/validator_tag.h @@ -29,22 +29,30 @@ #error "Please include only <sefht/validator.h>." #endif +#include <limits.h> #include <stdbool.h> #include <stdint.h> #include "status.h" -typedef unsigned int Tag; -#define TAG_ERR (Tag) 0 -#define TAG_MIN (Tag) 1 -#define TAG_MAX (Tag) SIZE_MAX +enum SH_VALIDATOR_TAG_TYPE +{ + SH_TAG_TYPE_UNDEFINED, + SH_TAG_TYPE_VOID, + SH_TAG_TYPE_TEMPLATE, + SH_TAG_TYPE_RAW_TEXT, + SH_TAG_TYPE_ESC_RAW_TEXT, + SH_TAG_TYPE_FOREIGN, + SH_TAG_TYPE_NORMAL, +}; -Tag +bool /*@alt void@*/ SH_Validator_register_tag (SH_Validator * validator, const char * tag, + enum SH_VALIDATOR_TAG_TYPE type, /*@null@*/ /*@out@*/ struct SH_Status * status) /*@modifies validator@*/ @@ -54,7 +62,7 @@ SH_Validator_register_tag (SH_Validator * validator, bool SH_Validator_deregister_tag (SH_Validator * validator, - Tag id, + const char * tag, /*@null@*/ /*@out@*/ struct SH_Status * status) /*@modifies validator@*/ @@ -63,8 +71,17 @@ SH_Validator_deregister_tag (SH_Validator * validator, /*@modifies status@*/; bool -SH_Validator_check_tag (struct SH_Validator * validator, +SH_Validator_check_tag (const SH_Validator * validator, const char * tag) /*@*/; +bool +SH_Validator_is_self_closing_tag (const SH_Validator * validator, + const char * tag, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + #endif /* SEFHT_VALIDATOR_TAG_H */ diff --git a/src/lib/sefht/validator_tag_data.h b/src/lib/sefht/validator_tag_data.h new file mode 100644 index 0000000000000000000000000000000000000000..2809cd0f687d7c588ce4c005f9615ac0191831eb --- /dev/null +++ b/src/lib/sefht/validator_tag_data.h @@ -0,0 +1,52 @@ +/* + * validator_tag_data.h + * + * Copyright 2023 Jonathan Schöbel <jonathan@xn--schbel-yxa.info> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + */ + + +#ifndef SEFHT_VALIDATOR_TAG_DATA_H +#define SEFHT_VALIDATOR_TAG_DATA_H + +#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION) +#error "Only <sefht/sefht.h> can be included directly." +#endif + +enum TAG_TYPE +{ + TAG_T_VOID, + TAG_T_TEMPLATE, + TAG_T_RAW_TEXT, + TAG_T_ESC_RAW_TEXT, + TAG_T_FOREIGN, + TAG_T_NORMAL, +}; + +struct tag_info +{ + /*@only@*/ char * name; + enum TAG_TYPE type; +}; + +#define TAG_DATA \ + /*@only@*/ struct tag_info * tags; \ + size_t tag_n; \ + +#endif /* SEFHT_VALIDATOR_TAG_DATA_H */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 158cbc51fabcd040cfcbc2f14dbeb72f2b1f1216..54d468b92eaed042e95a05866a6f934ed3fb91f8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -19,6 +19,9 @@ check_PROGRAMS += sefht_node_fragment_test check_PROGRAMS += sefht_text_fragment_test check_PROGRAMS += sefht_text_test check_PROGRAMS += sefht_text_mark_test +check_PROGRAMS += sefht_validator_test +check_PROGRAMS += sefht_validator_tag_test +check_PROGRAMS += sefht_validator_attr_test XFAIL_TESTS = XFAIL_TESTS += sefht_fragment_test @@ -84,3 +87,16 @@ sefht_text_test_LDADD += $(LDADD) sefht_text_mark_test_SOURCES = test_text_mark.c sefht_text_mark_test_LDADD = sefht_text_mark_test_LDADD += $(LDADD) + +sefht_validator_test_SOURCES = test_validator.c +sefht_validator_test_LDADD = +sefht_validator_test_LDADD += $(OBJECT_PREFIX)validator.o +sefht_validator_test_LDADD += $(LDADD) + +sefht_validator_tag_test_SOURCES = test_validator_tag.c +sefht_validator_tag_test_LDADD = +sefht_validator_tag_test_LDADD += $(LDADD) + +sefht_validator_attr_test_SOURCES = test_validator_attr.c +sefht_validator_attr_test_LDADD = +sefht_validator_attr_test_LDADD += $(LDADD) diff --git a/tests/test_node_fragment.c b/tests/test_node_fragment.c index 5d7f22f55d85944337ea87b8d6743069f72afb31..70673f2f59b00f704a79c8692930b29d402945f4 100644 --- a/tests/test_node_fragment.c +++ b/tests/test_node_fragment.c @@ -31,18 +31,37 @@ #include "node_fragment.c" +/* TODO: keep in sync with data.c + * TODO: remove */ +struct SH_Data +{ + SH_Validator * validator; +}; START_TEST(test_node_fragment_no_status) { struct SH_NodeFragment * fragment; SH_Data * data; const char * tag = "tag"; + const char * no_tag = "no_tag"; + bool result; + /* setup */ data = SH_Data_new (NULL); + ck_assert_ptr_ne (NULL, data); + + result = SH_Validator_register_tag (data->validator, tag, + SH_TAG_TYPE_NORMAL, NULL); + ck_assert_int_eq (TRUE, result); + /* test - invalid tag */ fragment = (struct SH_NodeFragment *) - SH_NodeFragment_new (tag, data, NULL); + SH_NodeFragment_new (no_tag, data, NULL); + ck_assert_ptr_eq (NULL, fragment); + /* test - valid tag */ + fragment = (struct SH_NodeFragment *) + SH_NodeFragment_new (tag, data, NULL); ck_assert_ptr_ne (NULL, fragment); ck_assert_ptr_eq (NULL, fragment->base.parent); @@ -54,8 +73,8 @@ START_TEST(test_node_fragment_no_status) ck_assert_int_eq (0, fragment->child_n); ck_assert_int_eq (0, fragment->child_s); + /* cleanup */ SH_NodeFragment_free (fragment); - SH_Data_free (data); } END_TEST @@ -66,13 +85,28 @@ START_TEST(test_node_fragment_with_status) struct SH_NodeFragment * fragment; SH_Data * data; const char * tag = "tag"; + const char * no_tag = "no_tag"; + bool result; + /* setup */ data = SH_Data_new (NULL); + ck_assert_ptr_ne (NULL, data); + + result = SH_Validator_register_tag (data->validator, tag, + SH_TAG_TYPE_NORMAL, NULL); + ck_assert_int_eq (TRUE, result); + /* test - invalid tag */ _status_preinit (status); fragment = (struct SH_NodeFragment *) - SH_NodeFragment_new (tag, data, &status); + SH_NodeFragment_new (no_tag, data, &status); + ck_assert_ptr_eq (NULL, fragment); + ck_assert_int_eq (E_VALUE, status.status); + /* test - valid tag */ + _status_preinit (status); + fragment = (struct SH_NodeFragment *) + SH_NodeFragment_new (tag, data, &status); ck_assert_ptr_ne (NULL, fragment); ck_assert (succeed (&status)); @@ -85,8 +119,8 @@ START_TEST(test_node_fragment_with_status) ck_assert_int_eq (0, fragment->child_n); ck_assert_int_eq (0, fragment->child_s); + /* cleanup */ SH_NodeFragment_free (fragment); - SH_Data_free (data); } END_TEST @@ -96,12 +130,25 @@ START_TEST(test_node_fragment_raw_no_status) struct SH_NodeFragment * fragment; SH_Data * data; char * tag = strdup ("tag"); + char * no_tag = strdup ("no_tag"); + bool result; + /* setup */ data = SH_Data_new (NULL); + ck_assert_ptr_ne (NULL, data); + result = SH_Validator_register_tag (data->validator, tag, + SH_TAG_TYPE_NORMAL, NULL); + ck_assert_int_eq (TRUE, result); + + /* test - invalid tag */ fragment = (struct SH_NodeFragment *) - SH_NodeFragment_raw_new (tag, data, NULL); + SH_NodeFragment_new (no_tag, data, NULL); + ck_assert_ptr_eq (NULL, fragment); + /* test - valid tag */ + fragment = (struct SH_NodeFragment *) + SH_NodeFragment_raw_new (tag, data, NULL); ck_assert_ptr_ne (NULL, fragment); ck_assert_ptr_eq (NULL, fragment->base.parent); @@ -113,9 +160,10 @@ START_TEST(test_node_fragment_raw_no_status) ck_assert_int_eq (0, fragment->child_n); ck_assert_int_eq (0, fragment->child_s); + /* cleanup */ SH_NodeFragment_free (fragment); - SH_Data_free (data); + free (no_tag); } END_TEST @@ -125,13 +173,28 @@ START_TEST(test_node_fragment_raw_with_status) struct SH_NodeFragment * fragment; SH_Data * data; char * tag = strdup ("tag"); + char * no_tag = strdup ("no_tag"); + bool result; + /* setup */ data = SH_Data_new (NULL); + ck_assert_ptr_ne (NULL, data); + + result = SH_Validator_register_tag (data->validator, tag, + SH_TAG_TYPE_NORMAL, NULL); + ck_assert_int_eq (TRUE, result); + /* test - invalid tag */ _status_preinit (status); fragment = (struct SH_NodeFragment *) - SH_NodeFragment_raw_new (tag, data, &status); + SH_NodeFragment_raw_new (no_tag, data, &status); + ck_assert_ptr_eq (NULL, fragment); + ck_assert_int_eq (E_VALUE, status.status); + /* test - valid tag */ + _status_preinit (status); + fragment = (struct SH_NodeFragment *) + SH_NodeFragment_raw_new (tag, data, &status); ck_assert_ptr_ne (NULL, fragment); ck_assert (succeed (&status)); @@ -144,9 +207,10 @@ START_TEST(test_node_fragment_raw_with_status) ck_assert_int_eq (0, fragment->child_n); ck_assert_int_eq (0, fragment->child_s); + /* cleanup */ SH_NodeFragment_free (fragment); - SH_Data_free (data); + free (no_tag); } END_TEST diff --git a/tests/test_validator.c b/tests/test_validator.c index e1a9e318fd6e1f6885e316789efe81e535bc741f..024e9064ad3223b131677642fd337efe77382125 100644 --- a/tests/test_validator.c +++ b/tests/test_validator.c @@ -28,216 +28,104 @@ #include <stdio.h> #include <stdlib.h> -/* test static method */ -#define static - -/* lower SIZE_MAX as we try to reach it */ -#include <limits.h> -#define SIZE_MAX 10 * sizeof (struct tag_info) #include "macro.h" #include "status.h" -/* C file is needed, because we wan't to override SIZE_MAX and static */ -#include "validator.c" +#include "validator.h" -START_TEST(test_validator) +START_TEST(test_validator_no_status) { - struct SH_Status status; struct SH_Validator * validator; validator = SH_Validator_new (NULL); - ck_assert_int_ne ((long int) validator, (long int) NULL); + ck_assert_ptr_ne (NULL, validator); SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; _status_preinit (status); validator = SH_Validator_new (&status); - ck_assert_int_ne ((long int) validator, (long int) NULL); + ck_assert_ptr_ne (NULL, validator); ck_assert_int_eq (status.status, SUCCESS); SH_Validator_free (validator); } END_TEST -START_TEST(test_validator_tag) +START_TEST(test_validator_copy_no_status) { - struct SH_Status status; struct SH_Validator * validator; - const char * tag1 = "html"; - const char * tag2 = "head"; - const char * tag3 = "body"; - const char * tag4 = "link"; - const char * tag5 = "main"; - const char * tag6 = "article"; - char * tagN; - Tag tag; - Tag tag_return; - bool check; + struct SH_Validator * copy; + /* setup */ validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); - /* success without error */ - tag = SH_Validator_register_tag (validator, tag1, NULL); - ck_assert_int_eq (tag, 1); - - ck_assert_int_eq (validator->tag_n, 1); - ck_assert_int_eq (validator->last_tag, tag); - - ck_assert_int_eq (validator->tags[1].data.id, tag); - ck_assert_str_eq (validator->tags[1].data.name, tag1); - - tag_return = get_tag_id_by_name (validator, tag1); - ck_assert_int_eq (tag_return, tag); - - /* retry without error */ - tag_return = SH_Validator_register_tag (validator, tag1, NULL); - ck_assert_int_eq (tag_return, tag); - - ck_assert_int_eq (validator->tag_n, 1); - ck_assert_int_eq (validator->last_tag, tag); - - ck_assert_int_eq (validator->tags[1].data.id, tag); - ck_assert_str_eq (validator->tags[1].data.name, tag1); - - /* fail without error */ - /* make method fail by filling with garbage until - * upper boundary is reached */ - - /* ensure enough space inside string*/ - /* log10 +1 = number length */ - /* +3 "tag" */ - /* +1 NULL */ - /* = +5 */ - tagN = calloc (((int) floor (log10 ((double) SIZE_MAX))) + 5, - sizeof (char)); - - /* fill with garbage */ - sprintf (tagN, "tag%lu", validator->tag_n); - while (SH_Validator_register_tag (validator, tagN, NULL) != TAG_ERR) - { - printf ("tag%lu\n", validator->tag_n); - sprintf (tagN, "tag%lu", validator->tag_n); - } - - free (tagN); - - tag = SH_Validator_register_tag (validator, tag2, NULL); - ck_assert_int_eq (tag, TAG_ERR); - - ck_assert_int_eq (validator->tag_n, 9); - - tag_return = get_tag_id_by_name (validator, tag2); - ck_assert_int_eq (tag_return, TAG_ERR); - - /* fail2 without error */ - validator->tag_n = 1; - validator->last_tag = TAG_MAX; - - tag = SH_Validator_register_tag (validator, tag3, NULL); - ck_assert_int_eq (tag, TAG_ERR); - - ck_assert_int_eq (validator->tag_n, 1); - ck_assert_int_eq (validator->last_tag, TAG_MAX); - - tag_return = get_tag_id_by_name (validator, tag3); - ck_assert_int_eq (tag_return, TAG_ERR); - - /* also free garbage created for overflow test */ - validator->tag_n = 9; - - /* check tag */ - check = SH_Validator_check_tag (validator, tag1); - ck_assert_int_eq (check, TRUE); - - check = SH_Validator_check_tag (validator, tag2); - ck_assert_int_eq (check, FALSE); - - check = SH_Validator_check_tag (validator, tag3); - ck_assert_int_eq (check, FALSE); + /* test */ + copy = SH_Validator_copy (validator, NULL); + ck_assert_ptr_ne (NULL, copy); + /* cleanup */ + SH_Validator_free (copy); SH_Validator_free (validator); +} +END_TEST +START_TEST(test_validator_copy_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + struct SH_Validator * copy; + /* setup */ validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); - /* success with error */ + /* test */ _status_preinit (status); - tag = SH_Validator_register_tag (validator, tag4, &status); - ck_assert_int_eq (tag, 1); - ck_assert_int_eq (status.status, SUCCESS); - - ck_assert_int_eq (validator->tag_n, 1); - ck_assert_int_eq (validator->last_tag, tag); - - ck_assert_int_eq (validator->tags[1].data.id, tag); - ck_assert_str_eq (validator->tags[1].data.name, tag4); - - tag_return = get_tag_id_by_name (validator, tag4); - ck_assert_int_eq (tag_return, tag); + copy = SH_Validator_copy (validator, &status); + ck_assert_ptr_ne (NULL, copy); + ck_assert_int_eq (SUCCESS, status.status); - /* fail with error */ - /* make method fail by filling with garbage until - * upper boundary is reached */ - - /* ensure enough space inside string*/ - /* log10 +1 = number length */ - /* +3 "tag" */ - /* +1 NULL */ - /* = +5 */ - tagN = calloc (((int) floor (log10 ((double) SIZE_MAX))) + 5, - sizeof (char)); - - /* fill with garbage */ - sprintf (tagN, "tag%lu", validator->tag_n); - while (SH_Validator_register_tag (validator, tagN, NULL) != TAG_ERR) - { - printf ("tag%lu\n", validator->tag_n); - sprintf (tagN, "tag%lu", validator->tag_n); - } - - free (tagN); + /* cleanup */ + SH_Validator_free (copy); + SH_Validator_free (validator); +} +END_TEST - _status_preinit (status); - tag = SH_Validator_register_tag (validator, tag5, &status); - ck_assert_int_eq (tag, TAG_ERR); - ck_assert_int_eq (status.status, E_DOMAIN); +START_TEST(test_validator_spec_no_status) +{ + struct SH_Validator * validator; - ck_assert_int_eq (validator->tag_n, 9); + /* test */ + validator = SH_Validator_new_html5 (NULL); + ck_assert_ptr_ne (NULL, validator); - tag_return = get_tag_id_by_name (validator, tag5); - ck_assert_int_eq (tag_return, TAG_ERR); + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST - /* fail2 with error */ - validator->tag_n = 1; - validator->last_tag = TAG_MAX; +START_TEST(test_validator_spec_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + /* test */ _status_preinit (status); - tag = SH_Validator_register_tag (validator, tag6, &status); - ck_assert_int_eq (tag, TAG_ERR); - ck_assert_int_eq (status.status, E_DOMAIN); - - ck_assert_int_eq (validator->tag_n, 1); - ck_assert_int_eq (validator->last_tag, TAG_MAX); - - tag_return = get_tag_id_by_name (validator, tag6); - ck_assert_int_eq (tag_return, TAG_ERR); - - - /* check tag */ - check = SH_Validator_check_tag (validator, tag4); - ck_assert_int_eq (check, TRUE); - - check = SH_Validator_check_tag (validator, tag5); - ck_assert_int_eq (check, FALSE); - - check = SH_Validator_check_tag (validator, tag6); - ck_assert_int_eq (check, FALSE); - - /* also free garbage created for overflow test */ - validator->tag_n = 9; + validator = SH_Validator_new_html5 (&status); + ck_assert_ptr_ne (NULL, validator); + ck_assert_int_eq (SUCCESS, status.status); + /* cleanup */ SH_Validator_free (validator); } END_TEST @@ -252,8 +140,12 @@ Suite * validator_suite (void) /* Core test case */ tc_core = tcase_create ("Core"); - tcase_add_test (tc_core, test_validator); - tcase_add_test (tc_core, test_validator_tag); + tcase_add_test (tc_core, test_validator_no_status); + 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); suite_add_tcase (s, tc_core); return s; diff --git a/tests/test_validator_attr.c b/tests/test_validator_attr.c new file mode 100644 index 0000000000000000000000000000000000000000..f18979118698b508f780d887be5e2e77dfeaf208 --- /dev/null +++ b/tests/test_validator_attr.c @@ -0,0 +1,1737 @@ +/* + * test_validator_attr.c + * + * Copyright 2023 Jonathan Schöbel <jonathan@xn--schbel-yxa.info> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + */ + + +#include <check.h> +#include <math.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + + +/* lower SIZE_MAX as we try to reach it */ +#include <limits.h> +#undef SIZE_MAX +#define SIZE_MAX 10 * sizeof (struct attr_info) + +#include "macro.h" +#include "status.h" + + +/* override HTML spec */ +#include "validator_html.h" + +const struct HTML_TAG_DEFINITION HTML_TAGS[] = { + {"html", SH_TAG_TYPE_NORMAL}, + {"aside", SH_TAG_TYPE_NORMAL}, + {"a", SH_TAG_TYPE_NORMAL}, + {"p", SH_TAG_TYPE_NORMAL}, +}; + +const struct HTML_ATTR_DEFINITION HTML_ATTRS[] = { + {"lang", &HTML_TAGS[0], 2}, + {"href", &HTML_TAGS[2], 1}, + {"class", &HTML_TAGS[3], 1}, + {"id", NULL, 0}, + {"class", NULL, 0}, +}; + +#define HTML5_TAGS HTML_TAGS +#define HTML5_ATTRS HTML_ATTRS + + +/* C file is needed, because we want to override SIZE_MAX */ +#include "validator.c" + + +START_TEST(test_validator_attr_no_status) +{ + struct SH_Validator * validator; + + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + ck_assert_int_eq (0, validator->attr_n); + + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_attr_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + + _status_preinit (status); + validator = SH_Validator_new (&status); + ck_assert_ptr_ne (NULL, validator); + ck_assert_int_eq (status.status, SUCCESS); + + ck_assert_int_eq (0, validator->attr_n); + + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_attr_copy_no_status) +{ + struct SH_Validator * validator; + struct SH_Validator * copy; + bool result; + + /* setup */ + validator = SH_Validator_new_html5 (NULL); + ck_assert_ptr_ne (NULL, validator); + + result = SH_Validator_register_attr (validator, NULL, "id", + NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_attr (validator, "html", "lang", + NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_attr (validator, "p", "lang", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test */ + copy = SH_Validator_copy (validator, NULL); + ck_assert_ptr_ne (NULL, copy); + + ck_assert_ptr_ne (NULL, copy->attrs); + ck_assert_ptr_ne (validator->attrs, copy->attrs); + ck_assert_int_eq (validator->attr_n, copy->attr_n); + + #define TEST_INT(I) ck_assert_int_eq (validator->I, copy->I); + #define TEST_PTR(P) ck_assert_ptr_eq (validator->P, copy->P); + #define TEST_STR(S) ck_assert_ptr_ne (validator->S, copy->S); \ + ck_assert_str_eq (validator->S, copy->S); + + for (size_t index = 0; index < copy->attr_n; index++) + { + TEST_STR(attrs[index].name) + + TEST_INT(attrs[index].tag_n) + for (size_t j = 0; j < copy->attrs[index].tag_n; j++) + { + TEST_PTR(attrs[index].tags[j].name) + } + + if (0 == validator->attrs[index].tag_n) + { + TEST_PTR(attrs[index].tags); + } + } + #undef TEST_INT + #undef TEST_PTR + #undef TEST_STR + + SH_Validator_free (copy); + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_attr_copy_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + struct SH_Validator * copy; + bool result; + + /* setup */ + validator = SH_Validator_new_html5 (NULL); + ck_assert_ptr_ne (NULL, validator); + + result = SH_Validator_register_attr (validator, NULL, "id", + NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_attr (validator, "html", "lang", + NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_attr (validator, "p", "lang", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test */ + _status_preinit (status); + copy = SH_Validator_copy (validator, &status); + ck_assert_ptr_ne (NULL, copy); + ck_assert_int_eq (status.status, SUCCESS); + + ck_assert_ptr_ne (NULL, copy->attrs); + ck_assert_ptr_ne (validator->attrs, copy->attrs); + ck_assert_int_eq (validator->attr_n, copy->attr_n); + + #define TEST_INT(I) ck_assert_int_eq (validator->I, copy->I); + #define TEST_PTR(P) ck_assert_ptr_eq (validator->P, copy->P); + #define TEST_STR(S) ck_assert_ptr_ne (validator->S, copy->S); \ + ck_assert_str_eq (validator->S, copy->S); + + for (size_t index = 0; index < copy->attr_n; index++) + { + TEST_STR(attrs[index].name) + + TEST_INT(attrs[index].tag_n) + for (size_t j = 0; j < copy->attrs[index].tag_n; j++) + { + TEST_PTR(attrs[index].tags[j].name) + } + + if (0 == validator->attrs[index].tag_n) + { + TEST_PTR(attrs[index].tags); + } + } + #undef TEST_INT + #undef TEST_PTR + #undef TEST_STR + + SH_Validator_free (copy); + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_attr_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 (4, validator->attr_n); + + #define attrs validator->attrs + + /* lang, 2 tags */ + ck_assert_str_eq ("lang", HTML_ATTRS[0].attr); + ck_assert_ptr_ne (HTML_ATTRS[0].attr, attrs[3].name); + ck_assert_str_eq (HTML_ATTRS[0].attr, attrs[3].name); + ck_assert_int_eq (HTML_ATTRS[0].tag_n, attrs[3].tag_n); + ck_assert_ptr_ne (NULL, attrs[3].tags); + + + #define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2); \ + ck_assert_str_eq (S1, S2); + /* 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[3].tags[0].name[0] + > attrs[3].tags[1].name[0]) + { + TEST_STR (HTML_TAGS[0].tag, attrs[3].tags[0].name); + TEST_STR (HTML_TAGS[1].tag, attrs[3].tags[1].name); + } + else + { + TEST_STR (HTML_TAGS[1].tag, attrs[3].tags[0].name); + TEST_STR (HTML_TAGS[0].tag, attrs[3].tags[1].name); + } + #undef TEST_STR + + /* href, 1 tag */ + ck_assert_str_eq ("href", HTML_ATTRS[1].attr); + ck_assert_ptr_ne (HTML_ATTRS[1].attr, attrs[1].name); + ck_assert_str_eq (HTML_ATTRS[1].attr, attrs[1].name); + ck_assert_int_eq (HTML_ATTRS[1].tag_n, attrs[1].tag_n); + ck_assert_ptr_ne (NULL, attrs[1].tags); + + ck_assert_ptr_ne (HTML_TAGS[2].tag, attrs[1].tags[0].name); + ck_assert_str_eq (HTML_TAGS[2].tag, attrs[1].tags[0].name); + + /* class, global, overwrite */ + ck_assert_str_eq ("class", HTML_ATTRS[2].attr); + ck_assert_ptr_ne (HTML_ATTRS[2].attr, attrs[0].name); + ck_assert_str_eq (HTML_ATTRS[2].attr, attrs[0].name); + ck_assert_int_eq (0, attrs[0].tag_n); + ck_assert_ptr_eq (NULL, attrs[0].tags); + + /* id, global */ + ck_assert_str_eq ("id", HTML_ATTRS[3].attr); + ck_assert_ptr_ne (HTML_ATTRS[3].attr, attrs[2].name); + ck_assert_str_eq (HTML_ATTRS[3].attr, attrs[2].name); + ck_assert_int_eq (0, attrs[2].tag_n); + ck_assert_ptr_eq (NULL, attrs[2].tags); + + #undef attrs + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_attr_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 (4, validator->attr_n); + + #define attrs validator->attrs + + /* lang, 2 tags */ + ck_assert_str_eq ("lang", HTML_ATTRS[0].attr); + ck_assert_ptr_ne (HTML_ATTRS[0].attr, attrs[3].name); + ck_assert_str_eq (HTML_ATTRS[0].attr, attrs[3].name); + ck_assert_int_eq (HTML_ATTRS[0].tag_n, attrs[3].tag_n); + ck_assert_ptr_ne (NULL, attrs[3].tags); + + + #define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2); \ + ck_assert_str_eq (S1, S2); + /* 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[3].tags[0].name[0] + > attrs[3].tags[1].name[0]) + { + TEST_STR (HTML_TAGS[0].tag, attrs[3].tags[0].name); + TEST_STR (HTML_TAGS[1].tag, attrs[3].tags[1].name); + } + else + { + TEST_STR (HTML_TAGS[1].tag, attrs[3].tags[0].name); + TEST_STR (HTML_TAGS[0].tag, attrs[3].tags[1].name); + } + #undef TEST_STR + + /* href, 1 tag */ + ck_assert_str_eq ("href", HTML_ATTRS[1].attr); + ck_assert_ptr_ne (HTML_ATTRS[1].attr, attrs[1].name); + ck_assert_str_eq (HTML_ATTRS[1].attr, attrs[1].name); + ck_assert_int_eq (HTML_ATTRS[1].tag_n, attrs[1].tag_n); + ck_assert_ptr_ne (NULL, attrs[1].tags); + + ck_assert_ptr_ne (HTML_TAGS[2].tag, attrs[1].tags[0].name); + ck_assert_str_eq (HTML_TAGS[2].tag, attrs[1].tags[0].name); + + /* class, global, overwrite */ + ck_assert_str_eq ("class", HTML_ATTRS[2].attr); + ck_assert_ptr_ne (HTML_ATTRS[2].attr, attrs[0].name); + ck_assert_str_eq (HTML_ATTRS[2].attr, attrs[0].name); + ck_assert_int_eq (0, attrs[0].tag_n); + ck_assert_ptr_eq (NULL, attrs[0].tags); + + /* id, global */ + ck_assert_str_eq ("id", HTML_ATTRS[3].attr); + ck_assert_ptr_ne (HTML_ATTRS[3].attr, attrs[2].name); + ck_assert_str_eq (HTML_ATTRS[3].attr, attrs[2].name); + ck_assert_int_eq (0, attrs[2].tag_n); + ck_assert_ptr_eq (NULL, attrs[2].tags); + + #undef attrs + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_attr_register_no_status) +{ + struct SH_Validator * validator; + const char * tag1 = "html"; + const char * tag2 = "form"; + const char * tag3 = "body"; + const char * tag4 = "nav"; + const char * tag5 = "aside"; + const char * notag = "foobarbaz"; // mustn't be registered + const char * attr1 = "id"; + const char * attr2 = "class"; + const char * attr3 = "name"; + const char * attr4 = "src"; + const char * attr5 = "content"; + const char * attr6 = "lang"; + char * attrN; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, tag1, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag2, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag3, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag4, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag5, V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + /* test - register */ + result = SH_Validator_register_attr (validator, tag1, attr1, + NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (1, validator->attr_n); + ck_assert_ptr_ne (attr1, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[0].name); + + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name); + ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name); + + /* test - duplicate registration */ + result = SH_Validator_register_attr (validator, tag1, attr1, + NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (1, validator->attr_n); + ck_assert_ptr_ne (attr1, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[0].name); + + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name); + ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name); + + /* test - register #2 */ + result = SH_Validator_register_attr (validator, tag2, attr1, + NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (1, validator->attr_n); + ck_assert_ptr_ne (attr1, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[0].name); + + /* test - register unknown tag */ + result = SH_Validator_register_attr (validator, notag, attr1, + NULL); + ck_assert_int_eq (FALSE, result); + + ck_assert_int_eq (1, validator->attr_n); + ck_assert_ptr_ne (attr1, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[0].name); + + #define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2); \ + ck_assert_str_eq (S1, S2); + + ck_assert_int_eq (2, validator->attrs[0].tag_n); + /* The storage order depends on the relative position of memory, + * allocated by different malloc calls. This can change and + * thus must be determined. */ + if (validator->attrs[0].tags[0].name[0] + > validator->attrs[0].tags[1].name[0]) + { + TEST_STR(tag1, validator->attrs[0].tags[0].name) + TEST_STR(tag2, validator->attrs[0].tags[1].name) + } + else + { + TEST_STR(tag2, validator->attrs[0].tags[0].name) + TEST_STR(tag1, validator->attrs[0].tags[1].name) + } + + #undef TEST_STR + + /* test - order (attr) */ + result = SH_Validator_register_attr (validator, tag1, attr3, + NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_attr (validator, tag1, attr4, + NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_attr (validator, tag1, attr5, + NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (4, validator->attr_n); + ck_assert_str_eq (attr5, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[1].name); + ck_assert_str_eq (attr3, validator->attrs[2].name); + ck_assert_str_eq (attr4, validator->attrs[3].name); + + /* test - order (tag) */ + result = SH_Validator_register_attr (validator, tag2, attr4, + NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_attr (validator, tag3, attr4, + NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_attr (validator, tag4, attr4, + NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_attr (validator, tag5, attr4, + NULL); + ck_assert_int_eq (TRUE, result); + + #define attr validator->attrs[3] + ck_assert_int_eq (5, attr.tag_n); + for (size_t i = 0; i < attr.tag_n-1; i++) + { + ck_assert_int_lt ((size_t)attr.tags[i].name, + (size_t)attr.tags[i+1].name); + } + #undef attr + + /* test - change to global attr */ + result = SH_Validator_register_attr (validator, NULL, attr1, + NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (4, validator->attr_n); + ck_assert_str_eq (attr5, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[1].name); + ck_assert_str_eq (attr3, validator->attrs[2].name); + ck_assert_str_eq (attr4, validator->attrs[3].name); + + ck_assert_int_eq (0, validator->attrs[1].tag_n); + ck_assert_ptr_eq (NULL, validator->attrs[1].tags); + + /* test - new global attr */ + result = SH_Validator_register_attr (validator, NULL, attr6, + NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (5, validator->attr_n); + ck_assert_str_eq (attr5, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[1].name); + ck_assert_str_eq (attr6, validator->attrs[2].name); + ck_assert_str_eq (attr3, validator->attrs[3].name); + ck_assert_str_eq (attr4, validator->attrs[4].name); + + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + + /* test - existing global attr */ + result = SH_Validator_register_attr (validator, tag1, attr6, + NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (5, validator->attr_n); + ck_assert_str_eq (attr5, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[1].name); + ck_assert_str_eq (attr6, validator->attrs[2].name); + ck_assert_str_eq (attr3, validator->attrs[3].name); + ck_assert_str_eq (attr4, validator->attrs[4].name); + + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + + /* test - overflow detection */ + /* make method fail by filling with garbage until + * upper boundary is reached */ + + /* ensure enough space inside string*/ + /* log10 +1 = number length */ + /* +4 "attr" */ + /* +1 '\0' */ + /* = +5 */ + attrN = malloc (((int) floor (log10 ((double) SIZE_MAX))) + 6); + ck_assert_ptr_ne (NULL, attrN); + + /* fill with garbage */ + do + { + sprintf (attrN, "attr%zu", validator->attr_n); + } + while (SH_Validator_register_attr (validator, tag1, attrN, NULL)); + + free (attrN); + + /* test overflow */ + result = SH_Validator_register_attr (validator, tag1, attr2, + NULL); + ck_assert_int_eq (FALSE, result); + + ck_assert_int_eq (10, validator->attr_n); + + /* cleanup */ + /* also free garbage created for overflow test */ + validator->attr_n = 10; + SH_Validator_free (validator); +} + +START_TEST(test_validator_attr_register_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + const char * tag1 = "html"; + const char * tag2 = "form"; + const char * tag3 = "body"; + const char * tag4 = "nav"; + const char * tag5 = "aside"; + const char * notag = "foobarbaz"; // mustn't be registered + const char * attr1 = "id"; + const char * attr2 = "class"; + const char * attr3 = "name"; + const char * attr4 = "src"; + const char * attr5 = "content"; + const char * attr6 = "lang"; + char * attrN; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, tag1, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag2, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag3, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag4, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag5, V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + /* test - register */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag1, attr1, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (1, validator->attr_n); + ck_assert_ptr_ne (attr1, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[0].name); + + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name); + ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name); + + /* test - duplicate registration */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag1, attr1, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (1, validator->attr_n); + ck_assert_ptr_ne (attr1, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[0].name); + + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name); + ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name); + + /* test - register #2 */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag2, attr1, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (1, validator->attr_n); + ck_assert_ptr_ne (attr1, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[0].name); + + /* test - register unknown tag */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, notag, attr1, + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_STATE, status.status); + + ck_assert_int_eq (1, validator->attr_n); + ck_assert_ptr_ne (attr1, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[0].name); + + #define TEST_STR(S1, S2) ck_assert_ptr_ne (S1, S2); \ + ck_assert_str_eq (S1, S2); + + ck_assert_int_eq (2, validator->attrs[0].tag_n); + /* The storage order depends on the relative position of memory, + * allocated by different malloc calls. This can change and + * thus must be determined. */ + if (validator->attrs[0].tags[0].name[0] + > validator->attrs[0].tags[1].name[0]) + { + TEST_STR(tag1, validator->attrs[0].tags[0].name) + TEST_STR(tag2, validator->attrs[0].tags[1].name) + } + else + { + TEST_STR(tag2, validator->attrs[0].tags[0].name) + TEST_STR(tag1, validator->attrs[0].tags[1].name) + } + + #undef TEST_STR + + /* test - order (attr) */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag1, attr3, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag1, attr4, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag1, attr5, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (4, validator->attr_n); + ck_assert_str_eq (attr5, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[1].name); + ck_assert_str_eq (attr3, validator->attrs[2].name); + ck_assert_str_eq (attr4, validator->attrs[3].name); + + /* test - order (tag) */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag2, attr4, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag3, attr4, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag4, attr4, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag5, attr4, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (5, validator->attrs[3].tag_n); + + #define attr validator->attrs[3] + ck_assert_int_eq (5, attr.tag_n); + for (size_t i = 0; i < attr.tag_n-1; i++) + { + ck_assert_int_lt ((size_t)attr.tags[i].name, + (size_t)attr.tags[i+1].name); + } + #undef attr + + /* test - change to global attr */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, NULL, attr1, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (4, validator->attr_n); + ck_assert_str_eq (attr5, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[1].name); + ck_assert_str_eq (attr3, validator->attrs[2].name); + ck_assert_str_eq (attr4, validator->attrs[3].name); + + ck_assert_int_eq (0, validator->attrs[1].tag_n); + ck_assert_ptr_eq (NULL, validator->attrs[1].tags); + + /* test - new global attr */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, NULL, attr6, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (5, validator->attr_n); + ck_assert_str_eq (attr5, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[1].name); + ck_assert_str_eq (attr6, validator->attrs[2].name); + ck_assert_str_eq (attr3, validator->attrs[3].name); + ck_assert_str_eq (attr4, validator->attrs[4].name); + + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + + /* test - existing global attr */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag1, attr6, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (5, validator->attr_n); + ck_assert_str_eq (attr5, validator->attrs[0].name); + ck_assert_str_eq (attr1, validator->attrs[1].name); + ck_assert_str_eq (attr6, validator->attrs[2].name); + ck_assert_str_eq (attr3, validator->attrs[3].name); + ck_assert_str_eq (attr4, validator->attrs[4].name); + + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + + /* test - overflow detection */ + /* make method fail by filling with garbage until + * upper boundary is reached */ + + /* ensure enough space inside string*/ + /* log10 +1 = number length */ + /* +4 "attr" */ + /* +1 '\0' */ + /* = +5 */ + attrN = malloc (((int) floor (log10 ((double) SIZE_MAX))) + 6); + ck_assert_ptr_ne (NULL, attrN); + + /* fill with garbage */ + do + { + sprintf (attrN, "attr%zu", validator->attr_n); + } + while (SH_Validator_register_attr (validator, tag1, attrN, NULL)); + + free (attrN); + + /* test overflow */ + _status_preinit (status); + result = SH_Validator_register_attr (validator, tag1, attr2, + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_DOMAIN, status.status); + + ck_assert_int_eq (10, validator->attr_n); + + /* cleanup */ + /* also free garbage created for overflow test */ + validator->attr_n = 10; + SH_Validator_free (validator); +} + +START_TEST(test_validator_attr_deregister_no_status) +{ + struct SH_Validator * validator; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, "html", V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, "body", V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + #define REGISTER SH_Validator_register_attr + result = REGISTER (validator, "html", "attr", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "html", "id", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "html", "name", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "html", "class", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "attr", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "id", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "name", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "class", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "v", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "w", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "x", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "y", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "z", NULL); + ck_assert_int_eq (TRUE, result); + #undef REGISTER + + /* test - consistency */ + ck_assert_int_eq (9, validator->attr_n); + ck_assert_str_eq ("attr", validator->attrs[0].name); + ck_assert_str_eq ("class", validator->attrs[1].name); + ck_assert_str_eq ("id", validator->attrs[2].name); + ck_assert_str_eq ("name", validator->attrs[3].name); + ck_assert_str_eq ("v", validator->attrs[4].name); + ck_assert_str_eq ("w", validator->attrs[5].name); + ck_assert_str_eq ("x", validator->attrs[6].name); + ck_assert_str_eq ("y", validator->attrs[7].name); + ck_assert_str_eq ("z", validator->attrs[8].name); + ck_assert_int_eq (2, validator->attrs[0].tag_n); + ck_assert_int_eq (2, validator->attrs[1].tag_n); + ck_assert_int_eq (2, validator->attrs[2].tag_n); + ck_assert_int_eq (2, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_int_eq (0, validator->attrs[6].tag_n); + ck_assert_int_eq (0, validator->attrs[7].tag_n); + ck_assert_int_eq (0, validator->attrs[8].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_ne (NULL, validator->attrs[2].tags); + ck_assert_ptr_ne (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_ptr_eq (NULL, validator->attrs[6].tags); + ck_assert_ptr_eq (NULL, validator->attrs[7].tags); + ck_assert_ptr_eq (NULL, validator->attrs[8].tags); + + #define STR_EQ ck_assert_str_eq + /* The storage order depends on the relative position of memory, + * allocated by different malloc calls. This can change and + * thus must be determined. */ + if (validator->attrs[0].tags[0].name[0] + > validator->attrs[0].tags[1].name[0]) + { + STR_EQ ("html", validator->attrs[0].tags[0].name); + STR_EQ ("html", validator->attrs[1].tags[0].name); + STR_EQ ("html", validator->attrs[2].tags[0].name); + STR_EQ ("html", validator->attrs[3].tags[0].name); + STR_EQ ("body", validator->attrs[0].tags[1].name); + STR_EQ ("body", validator->attrs[1].tags[1].name); + STR_EQ ("body", validator->attrs[2].tags[1].name); + STR_EQ ("body", validator->attrs[3].tags[1].name); + } + else + { + STR_EQ ("html", validator->attrs[0].tags[1].name); + STR_EQ ("html", validator->attrs[1].tags[1].name); + STR_EQ ("html", validator->attrs[2].tags[1].name); + STR_EQ ("html", validator->attrs[3].tags[1].name); + STR_EQ ("body", validator->attrs[0].tags[0].name); + STR_EQ ("body", validator->attrs[1].tags[0].name); + STR_EQ ("body", validator->attrs[2].tags[0].name); + STR_EQ ("body", validator->attrs[3].tags[0].name); + } + #undef STR_EQ + + + /* test - existent attr */ + result = SH_Validator_deregister_attr (validator, "html", "attr", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - non existent tag */ + result = SH_Validator_deregister_attr (validator, "html", "attr", + NULL); + ck_assert_int_eq (FALSE, result); + + /* test - existent attr, auto remove */ + result = SH_Validator_deregister_attr (validator, "body", "attr", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (8, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("name", validator->attrs[2].name); + ck_assert_str_eq ("v", validator->attrs[3].name); + ck_assert_str_eq ("w", validator->attrs[4].name); + ck_assert_str_eq ("x", validator->attrs[5].name); + ck_assert_str_eq ("y", validator->attrs[6].name); + ck_assert_str_eq ("z", validator->attrs[7].name); + ck_assert_int_eq (2, validator->attrs[0].tag_n); + ck_assert_int_eq (2, validator->attrs[1].tag_n); + ck_assert_int_eq (2, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_int_eq (0, validator->attrs[6].tag_n); + ck_assert_int_eq (0, validator->attrs[7].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_ne (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_ptr_eq (NULL, validator->attrs[6].tags); + ck_assert_ptr_eq (NULL, validator->attrs[7].tags); + + + /* test - non existent attr */ + result = SH_Validator_deregister_attr (validator, "html", "attr", + NULL); + ck_assert_int_eq (FALSE, result); + + /* test - existent attr, total remove */ + result = SH_Validator_deregister_attr (validator, NULL, "name", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - non existent attr, total remove */ + result = SH_Validator_deregister_attr (validator, NULL, "attr", + NULL); + ck_assert_int_eq (FALSE, result); + + /* test - consistency */ + ck_assert_int_eq (7, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("w", validator->attrs[3].name); + ck_assert_str_eq ("x", validator->attrs[4].name); + ck_assert_str_eq ("y", validator->attrs[5].name); + ck_assert_str_eq ("z", validator->attrs[6].name); + ck_assert_int_eq (2, validator->attrs[0].tag_n); + ck_assert_int_eq (2, validator->attrs[1].tag_n); + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_int_eq (0, validator->attrs[6].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_ptr_eq (NULL, validator->attrs[6].tags); + + + #define STR_EQ ck_assert_str_eq + /* The storage order depends on the relative position of memory, + * allocated by different malloc calls. This can change and + * thus must be determined. */ + if (validator->attrs[0].tags[0].name[0] + > validator->attrs[0].tags[1].name[0]) + { + STR_EQ ("html", validator->attrs[0].tags[0].name); + STR_EQ ("html", validator->attrs[1].tags[0].name); + STR_EQ ("body", validator->attrs[0].tags[1].name); + STR_EQ ("body", validator->attrs[1].tags[1].name); + } + else + { + STR_EQ ("html", validator->attrs[0].tags[1].name); + STR_EQ ("html", validator->attrs[1].tags[1].name); + STR_EQ ("body", validator->attrs[0].tags[0].name); + STR_EQ ("body", validator->attrs[1].tags[0].name); + } + #undef STR_EQ + + + /* test - existent tag, total remove */ + result = SH_Validator_deregister_attr (validator, "html", NULL, + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - non existent tag, total remove */ + result = SH_Validator_deregister_attr (validator, "nav", NULL, + NULL); + ck_assert_int_eq (FALSE, result); + + /* test - consistency */ + ck_assert_int_eq (7, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("w", validator->attrs[3].name); + ck_assert_str_eq ("x", validator->attrs[4].name); + ck_assert_str_eq ("y", validator->attrs[5].name); + ck_assert_str_eq ("z", validator->attrs[6].name); + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_int_eq (1, validator->attrs[1].tag_n); + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_int_eq (0, validator->attrs[6].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_ptr_eq (NULL, validator->attrs[6].tags); + ck_assert_str_eq ("body", validator->attrs[0].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[1].tags[0].name); + + /* test - global attr, to local */ + result = SH_Validator_deregister_attr (validator, "html", "v", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - global attr, non existent tag */ + result = SH_Validator_deregister_attr (validator, "nav", "w", + NULL); + ck_assert_int_eq (FALSE, result); + + /* test - global attr, total remove */ + result = SH_Validator_deregister_attr (validator, NULL, "w", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (6, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("x", validator->attrs[3].name); + ck_assert_str_eq ("y", validator->attrs[4].name); + ck_assert_str_eq ("z", validator->attrs[5].name); + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_int_eq (1, validator->attrs[1].tag_n); + ck_assert_int_eq (1, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_ne (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_str_eq ("body", validator->attrs[0].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[1].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[2].tags[0].name); + + /* test - global attr, auto remove */ + result = SH_Validator_deregister_tag (validator, "html", NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_deregister_attr (validator, "body", "x", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - global attr, non existent tag */ + result = SH_Validator_deregister_attr (validator, "nav", "y", + NULL); + ck_assert_int_eq (FALSE, result); + + /* test - global attr, total remove */ + result = SH_Validator_deregister_attr (validator, NULL, "y", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (4, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("z", validator->attrs[3].name); + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_int_eq (1, validator->attrs[1].tag_n); + ck_assert_int_eq (1, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_ne (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_str_eq ("body", validator->attrs[0].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[1].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[2].tags[0].name); + + /* test - last tag, auto remove local attrs */ + result = SH_Validator_deregister_tag (validator, "body", NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (1, validator->attr_n); + ck_assert_str_eq ("z", validator->attrs[0].name); + ck_assert_int_eq (0, validator->attrs[0].tag_n); + ck_assert_ptr_eq (NULL, validator->attrs[0].tags); + + /* undo auto remove */ + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, "body", V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + #define REGISTER SH_Validator_register_attr + result = REGISTER (validator, "body", "id", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "class", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "v", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "z", NULL); + ck_assert_int_eq (TRUE, result); + #undef REGISTER + + /* test - consistency */ + ck_assert_int_eq (4, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("z", validator->attrs[3].name); + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_int_eq (1, validator->attrs[1].tag_n); + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_str_eq ("body", validator->attrs[0].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[1].tags[0].name); + + /* test - total clear */ + result = SH_Validator_deregister_attr (validator, NULL, NULL, + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (0, validator->attr_n); + + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_attr_deregister_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, "html", V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, "body", V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + #define REGISTER SH_Validator_register_attr + result = REGISTER (validator, "html", "attr", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "html", "id", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "html", "name", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "html", "class", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "attr", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "id", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "name", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "class", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "v", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "w", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "x", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "y", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "z", NULL); + ck_assert_int_eq (TRUE, result); + #undef REGISTER + + /* test - consistency */ + ck_assert_int_eq (9, validator->attr_n); + ck_assert_str_eq ("attr", validator->attrs[0].name); + ck_assert_str_eq ("class", validator->attrs[1].name); + ck_assert_str_eq ("id", validator->attrs[2].name); + ck_assert_str_eq ("name", validator->attrs[3].name); + ck_assert_str_eq ("v", validator->attrs[4].name); + ck_assert_str_eq ("w", validator->attrs[5].name); + ck_assert_str_eq ("x", validator->attrs[6].name); + ck_assert_str_eq ("y", validator->attrs[7].name); + ck_assert_str_eq ("z", validator->attrs[8].name); + ck_assert_int_eq (2, validator->attrs[0].tag_n); + ck_assert_int_eq (2, validator->attrs[1].tag_n); + ck_assert_int_eq (2, validator->attrs[2].tag_n); + ck_assert_int_eq (2, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_int_eq (0, validator->attrs[6].tag_n); + ck_assert_int_eq (0, validator->attrs[7].tag_n); + ck_assert_int_eq (0, validator->attrs[8].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_ne (NULL, validator->attrs[2].tags); + ck_assert_ptr_ne (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_ptr_eq (NULL, validator->attrs[6].tags); + ck_assert_ptr_eq (NULL, validator->attrs[7].tags); + ck_assert_ptr_eq (NULL, validator->attrs[8].tags); + + #define STR_EQ ck_assert_str_eq + /* The storage order depends on the relative position of memory, + * allocated by different malloc calls. This can change and + * thus must be determined. */ + if (validator->attrs[0].tags[0].name[0] + > validator->attrs[0].tags[1].name[0]) + { + STR_EQ ("html", validator->attrs[0].tags[0].name); + STR_EQ ("html", validator->attrs[1].tags[0].name); + STR_EQ ("html", validator->attrs[2].tags[0].name); + STR_EQ ("html", validator->attrs[3].tags[0].name); + STR_EQ ("body", validator->attrs[0].tags[1].name); + STR_EQ ("body", validator->attrs[1].tags[1].name); + STR_EQ ("body", validator->attrs[2].tags[1].name); + STR_EQ ("body", validator->attrs[3].tags[1].name); + } + else + { + STR_EQ ("html", validator->attrs[0].tags[1].name); + STR_EQ ("html", validator->attrs[1].tags[1].name); + STR_EQ ("html", validator->attrs[2].tags[1].name); + STR_EQ ("html", validator->attrs[3].tags[1].name); + STR_EQ ("body", validator->attrs[0].tags[0].name); + STR_EQ ("body", validator->attrs[1].tags[0].name); + STR_EQ ("body", validator->attrs[2].tags[0].name); + STR_EQ ("body", validator->attrs[3].tags[0].name); + } + #undef STR_EQ + + + /* test - existent attr */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "html", "attr", + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - non existent tag */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "html", "attr", + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* test - existent attr, auto remove */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "body", "attr", + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - consistency */ + ck_assert_int_eq (8, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("name", validator->attrs[2].name); + ck_assert_str_eq ("v", validator->attrs[3].name); + ck_assert_str_eq ("w", validator->attrs[4].name); + ck_assert_str_eq ("x", validator->attrs[5].name); + ck_assert_str_eq ("y", validator->attrs[6].name); + ck_assert_str_eq ("z", validator->attrs[7].name); + ck_assert_int_eq (2, validator->attrs[0].tag_n); + ck_assert_int_eq (2, validator->attrs[1].tag_n); + ck_assert_int_eq (2, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_int_eq (0, validator->attrs[6].tag_n); + ck_assert_int_eq (0, validator->attrs[7].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_ne (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_ptr_eq (NULL, validator->attrs[6].tags); + ck_assert_ptr_eq (NULL, validator->attrs[7].tags); + + + /* test - non existent attr */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "html", "attr", + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* test - existent attr, total remove */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, NULL, "name", + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - non existent attr, total remove */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, NULL, "attr", + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* test - consistency */ + ck_assert_int_eq (7, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("w", validator->attrs[3].name); + ck_assert_str_eq ("x", validator->attrs[4].name); + ck_assert_str_eq ("y", validator->attrs[5].name); + ck_assert_str_eq ("z", validator->attrs[6].name); + ck_assert_int_eq (2, validator->attrs[0].tag_n); + ck_assert_int_eq (2, validator->attrs[1].tag_n); + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_int_eq (0, validator->attrs[6].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_ptr_eq (NULL, validator->attrs[6].tags); + + + #define STR_EQ ck_assert_str_eq + /* The storage order depends on the relative position of memory, + * allocated by different malloc calls. This can change and + * thus must be determined. */ + if (validator->attrs[0].tags[0].name[0] + > validator->attrs[0].tags[1].name[0]) + { + STR_EQ ("html", validator->attrs[0].tags[0].name); + STR_EQ ("html", validator->attrs[1].tags[0].name); + STR_EQ ("body", validator->attrs[0].tags[1].name); + STR_EQ ("body", validator->attrs[1].tags[1].name); + } + else + { + STR_EQ ("html", validator->attrs[0].tags[1].name); + STR_EQ ("html", validator->attrs[1].tags[1].name); + STR_EQ ("body", validator->attrs[0].tags[0].name); + STR_EQ ("body", validator->attrs[1].tags[0].name); + } + #undef STR_EQ + + + /* test - existent tag, total remove */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "html", NULL, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - non existent tag, total remove */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "nav", NULL, + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* test - consistency */ + ck_assert_int_eq (7, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("w", validator->attrs[3].name); + ck_assert_str_eq ("x", validator->attrs[4].name); + ck_assert_str_eq ("y", validator->attrs[5].name); + ck_assert_str_eq ("z", validator->attrs[6].name); + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_int_eq (1, validator->attrs[1].tag_n); + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_ptr_eq (NULL, validator->attrs[6].tags); + ck_assert_str_eq ("body", validator->attrs[0].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[1].tags[0].name); + + /* test - global attr, to local */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "html", "v", + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - global attr, non existent tag */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "nav", "w", + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* test - global attr, total remove */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, NULL, "w", + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - consistency */ + ck_assert_int_eq (6, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("x", validator->attrs[3].name); + ck_assert_str_eq ("y", validator->attrs[4].name); + ck_assert_str_eq ("z", validator->attrs[5].name); + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_int_eq (1, validator->attrs[1].tag_n); + ck_assert_int_eq (1, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_int_eq (0, validator->attrs[4].tag_n); + ck_assert_int_eq (0, validator->attrs[5].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_ne (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_ptr_eq (NULL, validator->attrs[4].tags); + ck_assert_ptr_eq (NULL, validator->attrs[5].tags); + ck_assert_str_eq ("body", validator->attrs[0].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[1].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[2].tags[0].name); + + /* test - global attr, auto remove */ + result = SH_Validator_deregister_tag (validator, "html", NULL); + ck_assert_int_eq (TRUE, result); + + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "body", "x", + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - global attr, non existent tag */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, "nav", "y", + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* test - global attr, total remove */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, NULL, "y", + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - consistency */ + ck_assert_int_eq (4, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("z", validator->attrs[3].name); + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_int_eq (1, validator->attrs[1].tag_n); + ck_assert_int_eq (1, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_ne (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_str_eq ("body", validator->attrs[0].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[1].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[2].tags[0].name); + + /* test - last tag, auto remove local attrs */ + result = SH_Validator_deregister_tag (validator, "body", NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (1, validator->attr_n); + ck_assert_str_eq ("z", validator->attrs[0].name); + ck_assert_int_eq (0, validator->attrs[0].tag_n); + ck_assert_ptr_eq (NULL, validator->attrs[0].tags); + + /* undo auto remove */ + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, "body", V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + #define REGISTER SH_Validator_register_attr + result = REGISTER (validator, "body", "id", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, "body", "class", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "v", NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, "z", NULL); + ck_assert_int_eq (TRUE, result); + #undef REGISTER + + /* test - consistency */ + ck_assert_int_eq (4, validator->attr_n); + ck_assert_str_eq ("class", validator->attrs[0].name); + ck_assert_str_eq ("id", validator->attrs[1].name); + ck_assert_str_eq ("v", validator->attrs[2].name); + ck_assert_str_eq ("z", validator->attrs[3].name); + ck_assert_int_eq (1, validator->attrs[0].tag_n); + ck_assert_int_eq (1, validator->attrs[1].tag_n); + ck_assert_int_eq (0, validator->attrs[2].tag_n); + ck_assert_int_eq (0, validator->attrs[3].tag_n); + ck_assert_ptr_ne (NULL, validator->attrs[0].tags); + ck_assert_ptr_ne (NULL, validator->attrs[1].tags); + ck_assert_ptr_eq (NULL, validator->attrs[2].tags); + ck_assert_ptr_eq (NULL, validator->attrs[3].tags); + ck_assert_str_eq ("body", validator->attrs[0].tags[0].name); + ck_assert_str_eq ("body", validator->attrs[1].tags[0].name); + + /* test - total clear */ + _status_preinit (status); + result = SH_Validator_deregister_attr (validator, NULL, NULL, + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - consistency */ + ck_assert_int_eq (0, validator->attr_n); + + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_attr_check) +{ + struct SH_Validator * validator; + const char * tag_1 = "html"; + const char * tag_2 = "head"; + const char * notag = "body"; + const char * attr_1 = "type"; + const char * attr_2 = "class"; + const char * attr_g = "id"; + const char * noattr = "name"; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, tag_1, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag_2, V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + #define REGISTER SH_Validator_register_attr + result = REGISTER (validator, tag_1, attr_1, NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, tag_2, attr_2, NULL); + ck_assert_int_eq (TRUE, result); + result = REGISTER (validator, NULL, attr_g, NULL); + ck_assert_int_eq (TRUE, result); + #undef REGISTER + + /* test */ + result = SH_Validator_check_attr (validator, tag_1, attr_1); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_check_attr (validator, tag_1, attr_2); + ck_assert_int_eq (FALSE, result); + + result = SH_Validator_check_attr (validator, tag_2, noattr); + ck_assert_int_eq (FALSE, result); + + result = SH_Validator_check_attr (validator, notag, attr_1); + ck_assert_int_eq (FALSE, result); + + result = SH_Validator_check_attr (validator, notag, noattr); + ck_assert_int_eq (FALSE, result); + + result = SH_Validator_check_attr (validator, NULL, attr_1); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_check_attr (validator, NULL, noattr); + ck_assert_int_eq (FALSE, result); + + result = SH_Validator_check_attr (validator, tag_1, attr_g); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_check_attr (validator, notag, attr_g); + ck_assert_int_eq (FALSE, result); + + result = SH_Validator_check_attr (validator, NULL, attr_g); + ck_assert_int_eq (TRUE, result); + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + +Suite * test_suite (void) +{ + Suite *s; + TCase *tc_core; + + s = suite_create ("Testsuite SeFHT Validator Attr"); + + /* Core test case */ + tc_core = tcase_create ("Core"); + + tcase_add_test (tc_core, test_validator_attr_no_status); + tcase_add_test (tc_core, test_validator_attr_with_status); + tcase_add_test (tc_core, test_validator_attr_copy_no_status); + tcase_add_test (tc_core, test_validator_attr_copy_with_status); + tcase_add_test (tc_core, test_validator_attr_spec_no_status); + tcase_add_test (tc_core, test_validator_attr_spec_with_status); + tcase_add_test (tc_core, test_validator_attr_register_no_status); + tcase_add_test (tc_core, test_validator_attr_register_with_status); + tcase_add_test (tc_core, test_validator_attr_deregister_no_status); + tcase_add_test (tc_core, test_validator_attr_deregister_with_status); + tcase_add_test (tc_core, test_validator_attr_check); + suite_add_tcase (s, tc_core); + + return s; +} + +int main (void) +{ + int number_failed; + Suite *s; + SRunner *sr; + + s = test_suite (); + sr = srunner_create (s); + + srunner_run_all (sr, CK_NORMAL); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + diff --git a/tests/test_validator_tag.c b/tests/test_validator_tag.c new file mode 100644 index 0000000000000000000000000000000000000000..61d9c5f0660ece201767b2e70095fa32f313da65 --- /dev/null +++ b/tests/test_validator_tag.c @@ -0,0 +1,732 @@ +/* + * test_validator_tag.c + * + * Copyright 2023 Jonathan Schöbel <jonathan@xn--schbel-yxa.info> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + */ + + +#include <check.h> +#include <math.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + + +/* lower SIZE_MAX as we try to reach it */ +#include <limits.h> +#undef SIZE_MAX +#define SIZE_MAX 10 * sizeof (struct tag_info) + +#include "macro.h" +#include "status.h" + +/* override HTML spec */ +#include "validator_html.h" +const struct HTML_TAG_DEFINITION HTML_TAGS[] = { + {"html", SH_TAG_TYPE_NORMAL}, + {"aside", SH_TAG_TYPE_ESC_RAW_TEXT}, + {"html", SH_TAG_TYPE_VOID}, + {"body", SH_TAG_TYPE_TEMPLATE} +}; +#define HTML5_TAGS HTML_TAGS + +/* C file is needed, because we want to override SIZE_MAX */ +#include "validator.c" + + +START_TEST(test_validator_tag_no_status) +{ + struct SH_Validator * validator; + + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + ck_assert_int_eq (0, validator->tag_n); + + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_tag_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + + _status_preinit (status); + validator = SH_Validator_new (&status); + ck_assert_ptr_ne (NULL, validator); + ck_assert_int_eq (status.status, SUCCESS); + + ck_assert_int_eq (0, validator->tag_n); + + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_tag_copy_no_status) +{ + struct SH_Validator * validator; + struct SH_Validator * copy; + + /* setup */ + validator = SH_Validator_new_html5 (NULL); + ck_assert_ptr_ne (NULL, validator); + + /* test */ + copy = SH_Validator_copy (validator, NULL); + ck_assert_ptr_ne (NULL, copy); + + ck_assert_ptr_ne (NULL, copy->tags); + ck_assert_ptr_ne (validator->tags, copy->tags); + ck_assert_int_eq (validator->tag_n, copy->tag_n); + + #define TEST_STR(S) ck_assert_ptr_ne (validator->S, copy->S); \ + ck_assert_str_eq (validator->S, copy->S); + #define TEST_INT(I) ck_assert_int_eq (validator->I, copy->I); + + for (size_t index = 0; index < copy->tag_n; index++) + { + TEST_STR (tags[index].name); + TEST_INT (tags[index].type); + } + + #undef TEST_STR + + SH_Validator_free (copy); + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_tag_copy_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + struct SH_Validator * copy; + + /* setup */ + validator = SH_Validator_new_html5 (NULL); + ck_assert_ptr_ne (NULL, validator); + + /* test */ + _status_preinit (status); + copy = SH_Validator_copy (validator, &status); + ck_assert_ptr_ne (NULL, copy); + ck_assert_int_eq (status.status, SUCCESS); + + ck_assert_ptr_ne (NULL, copy->tags); + ck_assert_ptr_ne (validator->tags, copy->tags); + ck_assert_int_eq (validator->tag_n, copy->tag_n); + + #define TEST_STR(S) ck_assert_ptr_ne (validator->S, copy->S); \ + ck_assert_str_eq (validator->S, copy->S); + #define TEST_INT(I) ck_assert_int_eq (validator->I, copy->I); + + for (size_t index = 0; index < copy->tag_n; index++) + { + TEST_STR (tags[index].name); + TEST_INT (tags[index].type); + } + + #undef TEST_STR + + SH_Validator_free (copy); + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_tag_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->tags); + ck_assert_int_eq (3, validator->tag_n); + + ck_assert_ptr_ne (HTML_TAGS[1].tag, validator->tags[0].name); + ck_assert_str_eq (HTML_TAGS[1].tag, validator->tags[0].name); + ck_assert_int_eq (TAG_T_ESC_RAW_TEXT, validator->tags[0].type); + + ck_assert_ptr_ne (HTML_TAGS[3].tag, validator->tags[1].name); + ck_assert_str_eq (HTML_TAGS[3].tag, validator->tags[1].name); + ck_assert_int_eq (TAG_T_TEMPLATE, validator->tags[1].type); + + ck_assert_ptr_ne (HTML_TAGS[0].tag, validator->tags[2].name); + ck_assert_str_eq (HTML_TAGS[0].tag, validator->tags[2].name); + ck_assert_int_eq (TAG_T_NORMAL, validator->tags[2].type); + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_tag_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->tags); + ck_assert_int_eq (3, validator->tag_n); + + ck_assert_ptr_ne (HTML_TAGS[1].tag, validator->tags[0].name); + ck_assert_str_eq (HTML_TAGS[1].tag, validator->tags[0].name); + ck_assert_int_eq (TAG_T_ESC_RAW_TEXT, validator->tags[0].type); + + ck_assert_ptr_ne (HTML_TAGS[3].tag, validator->tags[1].name); + ck_assert_str_eq (HTML_TAGS[3].tag, validator->tags[1].name); + ck_assert_int_eq (TAG_T_TEMPLATE, validator->tags[1].type); + + ck_assert_ptr_ne (HTML_TAGS[0].tag, validator->tags[2].name); + ck_assert_str_eq (HTML_TAGS[0].tag, validator->tags[2].name); + ck_assert_int_eq (TAG_T_NORMAL, validator->tags[2].type); + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_tag_register_no_status) +{ + struct SH_Validator * validator; + const char * tag1 = "html"; + const char * tag2 = "head"; + const char * tag3 = "article"; + const char * tag4 = "p"; + const char * tag5 = "img"; + char * tagN; + bool result; + + #define V SH_TAG_TYPE_VOID + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + /* test - register */ + result = SH_Validator_register_tag (validator, tag1, + SH_TAG_TYPE_VOID, NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (1, validator->tag_n); + ck_assert_ptr_ne (tag1, validator->tags[0].name); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_int_eq (TAG_T_VOID, validator->tags[0].type); + + /* test - invalid type */ + result = SH_Validator_register_tag (validator, tag2, -1, NULL); + ck_assert_int_eq (FALSE, result); + + /* test - duplicate registration */ + result = SH_Validator_register_tag (validator, tag1, + SH_TAG_TYPE_NORMAL, NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (1, validator->tag_n); + ck_assert_ptr_ne (tag1, validator->tags[0].name); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_int_eq (TAG_T_NORMAL, validator->tags[0].type); + + /* test - duplicate invalid type */ + result = SH_Validator_register_tag (validator, tag1, 20, NULL); + ck_assert_int_eq (FALSE, result); + + /* test - order */ + result = SH_Validator_register_tag (validator, tag3, V, NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_tag (validator, tag4, V, NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_tag (validator, tag5, V, NULL); + ck_assert_int_eq (TRUE, result); + + ck_assert_int_eq (4, validator->tag_n); + ck_assert_str_eq (tag3, validator->tags[0].name); + ck_assert_str_eq (tag1, validator->tags[1].name); + ck_assert_str_eq (tag5, validator->tags[2].name); + ck_assert_str_eq (tag4, validator->tags[3].name); + + /* test - overflow detection */ + /* make method fail by filling with garbage until + * upper boundary is reached */ + + /* ensure enough space inside string*/ + /* log10 +1 = number length */ + /* +3 "tag" */ + /* +1 '\0' */ + /* = +5 */ + tagN = malloc (((int) floor (log10 ((double) SIZE_MAX))) + 5); + ck_assert_ptr_ne (NULL, tagN); + + /* fill with garbage */ + do + { + sprintf (tagN, "tag%zu", validator->tag_n); + } + while (SH_Validator_register_tag (validator, tagN, V, NULL)); + + free (tagN); + + /* test overflow */ + result = SH_Validator_register_tag (validator, tag2, V, NULL); + ck_assert_int_eq (FALSE, result); + + ck_assert_int_eq (10, validator->tag_n); + + #undef V + + /* cleanup */ + /* also free garbage created for overflow test */ + validator->tag_n = 10; + SH_Validator_free (validator); +} + +START_TEST(test_validator_tag_register_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + const char * tag1 = "html"; + const char * tag2 = "head"; + const char * tag3 = "article"; + const char * tag4 = "p"; + const char * tag5 = "img"; + char * tagN; + bool result; + + #define V SH_TAG_TYPE_VOID + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + /* test - register */ + _status_preinit (status); + result = SH_Validator_register_tag (validator, tag1, + SH_TAG_TYPE_VOID, &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (1, validator->tag_n); + ck_assert_ptr_ne (tag1, validator->tags[0].name); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_int_eq (TAG_T_VOID, validator->tags[0].type); + + /* test - invalid type */ + _status_preinit (status); + result = SH_Validator_register_tag (validator, tag2, -1, &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* test - duplicate registration */ + _status_preinit (status); + result = SH_Validator_register_tag (validator, tag1, + SH_TAG_TYPE_NORMAL, &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + ck_assert_int_eq (1, validator->tag_n); + ck_assert_ptr_ne (tag1, validator->tags[0].name); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_int_eq (TAG_T_NORMAL, validator->tags[0].type); + + /* test - duplicate invalid type */ + _status_preinit (status); + result = SH_Validator_register_tag (validator, tag1, 20, &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* test - order */ + _status_preinit (status); + result = SH_Validator_register_tag (validator, tag3, V, &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (status.status, SUCCESS); + + _status_preinit (status); + result = SH_Validator_register_tag (validator, tag4, V, &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (status.status, SUCCESS); + + _status_preinit (status); + result = SH_Validator_register_tag (validator, tag5, V, &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (status.status, SUCCESS); + + ck_assert_int_eq (4, validator->tag_n); + ck_assert_str_eq (tag3, validator->tags[0].name); + ck_assert_str_eq (tag1, validator->tags[1].name); + ck_assert_str_eq (tag5, validator->tags[2].name); + ck_assert_str_eq (tag4, validator->tags[3].name); + + /* test - overflow detection */ + /* make method fail by filling with garbage until + * upper boundary is reached */ + + /* ensure enough space inside string*/ + /* log10 +1 = number length */ + /* +3 "tag" */ + /* +1 '\0' */ + /* = +5 */ + tagN = malloc (((int) floor (log10 ((double) SIZE_MAX))) + 5); + ck_assert_ptr_ne (NULL, tagN); + + /* fill with garbage */ + do + { + sprintf (tagN, "tag%zu", validator->tag_n); + } + while (SH_Validator_register_tag (validator, tagN, V, NULL)); + + free (tagN); + + /* test overflow */ + _status_preinit (status); + result = SH_Validator_register_tag (validator, tag2, V, &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_DOMAIN, status.status); + + ck_assert_int_eq (validator->tag_n, 10); + + #undef V + + /* cleanup */ + /* also free garbage created for overflow test */ + validator->tag_n = 10; + SH_Validator_free (validator); +} + +START_TEST(test_validator_tag_deregister_no_status) +{ + struct SH_Validator * validator; + const char * tag1 = "tag1"; + const char * tag2 = "tag2"; + const char * tag3 = "tag3"; + const char * attr = "attr"; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, tag1, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag2, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag3, V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + result = SH_Validator_register_attr (validator, tag1, attr, + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (3, validator->tag_n); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_str_eq (tag2, validator->tags[1].name); + ck_assert_str_eq (tag3, validator->tags[2].name); + + /* test - non existent */ + result = SH_Validator_deregister_tag (validator, "notag", NULL); + ck_assert_int_eq (FALSE, result); + + /* test - consistency */ + ck_assert_int_eq (3, validator->tag_n); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_str_eq (tag2, validator->tags[1].name); + ck_assert_str_eq (tag3, validator->tags[2].name); + + /* test - existent #1 */ + result = SH_Validator_deregister_tag (validator, tag2, NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (2, validator->tag_n); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_str_eq (tag3, validator->tags[1].name); + + /* test - existent #2, automatic attr deregister */ + result = SH_Validator_deregister_tag (validator, tag1, NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_check_attr (validator, NULL, attr); + ck_assert_int_eq (FALSE, result); + + /* test - consistency */ + ck_assert_int_eq (1, validator->tag_n); + ck_assert_str_eq (tag3, validator->tags[0].name); + + /* test - existent #3 */ + result = SH_Validator_deregister_tag (validator, tag3, NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (0, validator->tag_n); + + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_tag_deregister_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + const char * tag1 = "tag1"; + const char * tag2 = "tag2"; + const char * tag3 = "tag3"; + const char * attr = "attr"; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, tag1, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag2, V, NULL); + ck_assert_int_eq (TRUE, result); + result = SH_Validator_register_tag (validator, tag3, V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + result = SH_Validator_register_attr (validator, tag1, attr, + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - consistency */ + ck_assert_int_eq (3, validator->tag_n); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_str_eq (tag2, validator->tags[1].name); + ck_assert_str_eq (tag3, validator->tags[2].name); + + /* test - non existent */ + _status_preinit (status); + result = SH_Validator_deregister_tag (validator, "notag", + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* test - consistency */ + ck_assert_int_eq (3, validator->tag_n); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_str_eq (tag2, validator->tags[1].name); + ck_assert_str_eq (tag3, validator->tags[2].name); + + /* test - existent #1 */ + _status_preinit (status); + result = SH_Validator_deregister_tag (validator, tag2, &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - consistency */ + ck_assert_int_eq (2, validator->tag_n); + ck_assert_str_eq (tag1, validator->tags[0].name); + ck_assert_str_eq (tag3, validator->tags[1].name); + + /* test - existent #2, automatic attr deregister */ + _status_preinit (status); + result = SH_Validator_deregister_tag (validator, tag1, &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + result = SH_Validator_check_attr (validator, NULL, attr); + ck_assert_int_eq (FALSE, result); + + /* test - consistency */ + ck_assert_int_eq (1, validator->tag_n); + ck_assert_str_eq (tag3, validator->tags[0].name); + + /* test - existent #3 */ + _status_preinit (status); + result = SH_Validator_deregister_tag (validator, tag3, &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - consistency */ + ck_assert_int_eq (0, validator->tag_n); + + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_tag_check) +{ + struct SH_Validator * validator; + const char * tag1 = "html"; + const char * tag2 = "html"; + const char * tag3 = "head"; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + #define V SH_TAG_TYPE_VOID + result = SH_Validator_register_tag (validator, tag1, V, NULL); + ck_assert_int_eq (TRUE, result); + #undef V + + /* test */ + result = SH_Validator_check_tag (validator, tag1); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_check_tag (validator, tag2); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_check_tag (validator, tag3); + ck_assert_int_eq (FALSE, result); + + /* cleanup */ + SH_Validator_free (validator); +} +END_TEST + +START_TEST(test_validator_tag_self_closing_no_status) +{ + struct SH_Validator * validator; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + result = SH_Validator_register_tag (validator, "html", + SH_TAG_TYPE_NORMAL, NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_tag (validator, "link", + SH_TAG_TYPE_VOID, NULL); + ck_assert_int_eq (TRUE, result); + + /* test - self closing */ + result = SH_Validator_is_self_closing_tag (validator, "link", + NULL); + ck_assert_int_eq (TRUE, result); + + /* test - not self closing */ + result = SH_Validator_is_self_closing_tag (validator, "html", + NULL); + ck_assert_int_eq (FALSE, result); + + /* test - invalid tag */ + result = SH_Validator_is_self_closing_tag (validator, "body", + NULL); + ck_assert_int_eq (FALSE, result); + + /* cleanup */ + SH_Validator_free (validator); +} + +START_TEST(test_validator_tag_self_closing_with_status) +{ + struct SH_Status status; + struct SH_Validator * validator; + bool result; + + /* setup */ + validator = SH_Validator_new (NULL); + ck_assert_ptr_ne (NULL, validator); + + result = SH_Validator_register_tag (validator, "html", + SH_TAG_TYPE_NORMAL, NULL); + ck_assert_int_eq (TRUE, result); + + result = SH_Validator_register_tag (validator, "link", + SH_TAG_TYPE_VOID, NULL); + ck_assert_int_eq (TRUE, result); + + /* test - self closing */ + _status_preinit (status); + result = SH_Validator_is_self_closing_tag (validator, "link", + &status); + ck_assert_int_eq (TRUE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - not self closing */ + _status_preinit (status); + result = SH_Validator_is_self_closing_tag (validator, "html", + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (SUCCESS, status.status); + + /* test - invalid tag */ + _status_preinit (status); + result = SH_Validator_is_self_closing_tag (validator, "body", + &status); + ck_assert_int_eq (FALSE, result); + ck_assert_int_eq (E_VALUE, status.status); + + /* cleanup */ + SH_Validator_free (validator); +} + +Suite * test_suite (void) +{ + Suite *s; + TCase *tc_core; + + s = suite_create ("Testsuite SeFHT Validator Tag"); + + /* Core test case */ + tc_core = tcase_create ("Core"); + + tcase_add_test (tc_core, test_validator_tag_no_status); + tcase_add_test (tc_core, test_validator_tag_with_status); + tcase_add_test (tc_core, test_validator_tag_copy_no_status); + tcase_add_test (tc_core, test_validator_tag_copy_with_status); + tcase_add_test (tc_core, test_validator_tag_spec_no_status); + tcase_add_test (tc_core, test_validator_tag_spec_with_status); + tcase_add_test (tc_core, test_validator_tag_register_no_status); + tcase_add_test (tc_core, test_validator_tag_register_with_status); + tcase_add_test (tc_core, test_validator_tag_deregister_no_status); + tcase_add_test (tc_core, test_validator_tag_deregister_with_status); + tcase_add_test (tc_core, test_validator_tag_check); + tcase_add_test (tc_core, test_validator_tag_self_closing_no_status); + tcase_add_test (tc_core, test_validator_tag_self_closing_with_status); + suite_add_tcase (s, tc_core); + + return s; +} + +int main (void) +{ + int number_failed; + Suite *s; + SRunner *sr; + + s = test_suite (); + sr = srunner_create (s); + + srunner_run_all (sr, CK_NORMAL); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + diff --git a/todo.txt b/todo.txt index 0de5b07d491a360e2d6eeda4d7085fd3463d95a4..c2bf52062f518850fd659fd4ef11c8b030ef5684 100644 --- a/todo.txt +++ b/todo.txt @@ -2,14 +2,9 @@ create Logger create Docs -dynamic Validator initialization - remove -Wno-nonnull from AM_CFLAGS fix warnings for tests -rewrite validator test -restructure validator - Fragment: - create html on single Text object - add customized styling in html generation @@ -20,3 +15,8 @@ Fragment: - support of newlines (72/79/80) - support of escaping html symbols (but only which are special in context) - support for mapping special chars -> html entities + +Validator: +- check for global attributes +- fix cursed behaviour, when removing fails (remove_tag_for_all_attrs) +- initialize from file