From 55bbb701edacdb0491f839237933cd54721fca77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonathan=20Sch=C3=B6bel?= <jonathan@xn--schbel-yxa.info>
Date: Wed, 4 Oct 2023 15:00:07 +0200
Subject: [PATCH] Validator: added tag types

Each tag has a type as defined by the html spec. This must be provided
on registration. Implicitly registering tags, when an attribute is
registered can't be done anymore, as the type information would be
missing.
The added parameterin register_tag, as well as the change of behaviourin
register_attr has broken a lot of tests, that had to be adjusted
therefor.
---
 sefht.geany                        |  22 +++----
 src/lib/sefht/data.c               |  58 +++++++++-------
 src/lib/sefht/validator_attr.c     |   8 +--
 src/lib/sefht/validator_html.h     |   4 ++
 src/lib/sefht/validator_tag.c      |  94 +++++++++++++++++++++++++-
 src/lib/sefht/validator_tag.h      |  13 ++++
 src/lib/sefht/validator_tag_data.h |  11 ++++
 tests/test_node_fragment.c         |  12 ++--
 tests/test_validator_attr.c        |  94 +++++++++++++++++++++++---
 tests/test_validator_tag.c         | 102 ++++++++++++++++++++++-------
 todo.txt                           |   1 -
 11 files changed, 340 insertions(+), 79 deletions(-)

diff --git a/sefht.geany b/sefht.geany
index 4ab8395..e976bd0 100644
--- a/sefht.geany
+++ b/sefht.geany
@@ -28,7 +28,7 @@ long_line_behaviour=1
 long_line_column=72
 
 [files]
-current_page=8
+current_page=53
 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
@@ -37,7 +37,7 @@ FILE_NAME_4=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm
 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=4132;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fdata.c;0;8
+FILE_NAME_8=4584;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=1794;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
@@ -60,11 +60,11 @@ FILE_NAME_27=900;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fpr
 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=2191;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator.c;0;8
 FILE_NAME_30=1159;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator.h;0;8
-FILE_NAME_31=1303;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_html.h;0;8
-FILE_NAME_32=10846;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_tag.c;0;8
-FILE_NAME_33=1068;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_tag.h;0;8
-FILE_NAME_34=1287;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_tag_data.h;0;8
-FILE_NAME_35=19943;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_31=1008;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=14105;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=1251;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=22399;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_attr.c;0;8
 FILE_NAME_36=1051;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_attr.h;0;8
 FILE_NAME_37=1413;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator_attr_data.h;0;8
 FILE_NAME_38=924;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fstatus.h;0;8
@@ -76,15 +76,15 @@ FILE_NAME_43=218;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fp
 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=4446;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_node_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=2447;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8
-FILE_NAME_53=16197;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=57386;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=85;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftodo.txt;0;8
+FILE_NAME_53=9430;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=201;YAML;0;EUTF-8;0;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2F.gitlab-ci.yml;0;4
 FILE_NAME_57=71;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fgitlab-ci%2Fupload.sh.in;0;8
 FILE_NAME_58=806;Sh;0;EUTF-8;0;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fgitlab-ci%2Frelease.sh.in;0;4
diff --git a/src/lib/sefht/data.c b/src/lib/sefht/data.c
index 1ca38c0..8211a31 100644
--- a/src/lib/sefht/data.c
+++ b/src/lib/sefht/data.c
@@ -168,31 +168,39 @@ init_validator (struct SH_Data * data,
 		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, "nav", 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);
+	#define R(...)                                                 \
+	if (!SH_Validator_register_tag (data->validator,               \
+	                                __VA_ARGS__, NULL))            \
+	{                                                              \
+		SH_Validator_free (data->validator);                   \
+		return FALSE;                                          \
+	}
+	R("html", SH_TAG_TYPE_NORMAL);
+	R("head", SH_TAG_TYPE_NORMAL);
+	R("body", SH_TAG_TYPE_NORMAL);
+	R("meta", SH_TAG_TYPE_VOID);
+	R("link", SH_TAG_TYPE_VOID);
+	R("title", SH_TAG_TYPE_ESC_RAW_TEXT);
+	R("main", SH_TAG_TYPE_NORMAL);
+	R("article", SH_TAG_TYPE_NORMAL);
+	R("section", SH_TAG_TYPE_NORMAL);
+	R("header", SH_TAG_TYPE_NORMAL);
+	R("footer", SH_TAG_TYPE_NORMAL);
+	R("nav", SH_TAG_TYPE_NORMAL);
+	R("h1", SH_TAG_TYPE_NORMAL);
+	R("h2", SH_TAG_TYPE_NORMAL);
+	R("h3", SH_TAG_TYPE_NORMAL);
+	R("h4", SH_TAG_TYPE_NORMAL);
+	R("h5", SH_TAG_TYPE_NORMAL);
+	R("h6", SH_TAG_TYPE_NORMAL);
+	R("p", SH_TAG_TYPE_NORMAL);
+	R("br", SH_TAG_TYPE_VOID);
+	R("i", SH_TAG_TYPE_NORMAL);
+	R("b", SH_TAG_TYPE_NORMAL);
+	R("strong", SH_TAG_TYPE_NORMAL);
+	R("em", SH_TAG_TYPE_NORMAL);
+	R("small", SH_TAG_TYPE_NORMAL);
+	#undef R
 
 	return TRUE;
 }
diff --git a/src/lib/sefht/validator_attr.c b/src/lib/sefht/validator_attr.c
index b566c6b..3482f12 100644
--- a/src/lib/sefht/validator_attr.c
+++ b/src/lib/sefht/validator_attr.c
@@ -286,9 +286,9 @@ init_attr_from_spec (/*@special@*/ struct SH_Validator * validator,
 		const char * tag;
 
 		tag = spec.tags[index].tag;
-		if ((!find_tag (validator, tag, &position))
-		&& (!add_tag (validator, tag, position, status)))
+		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;
@@ -969,9 +969,9 @@ SH_Validator_register_attr (struct SH_Validator * validator,
 
 	if (NULL != tag)
 	{
-		if ((!find_tag (validator, tag, &index))
-		&& (!add_tag (validator, tag, index, status)))
+		if (!find_tag (validator, tag, &index))
 		{
+			set_status (status, E_STATE, 2, "no such tag");
 			return FALSE;
 		}
 
diff --git a/src/lib/sefht/validator_html.h b/src/lib/sefht/validator_html.h
index f952eb2..79268b5 100644
--- a/src/lib/sefht/validator_html.h
+++ b/src/lib/sefht/validator_html.h
@@ -29,9 +29,13 @@
 #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
diff --git a/src/lib/sefht/validator_tag.c b/src/lib/sefht/validator_tag.c
index ebd6e7a..388d903 100644
--- a/src/lib/sefht/validator_tag.c
+++ b/src/lib/sefht/validator_tag.c
@@ -36,6 +36,18 @@
 #include "validator_html.h"
 
 
+static inline
+bool
+tag_type_to_internal (/*@out@*/ enum TAG_TYPE * internal,
+                      enum SH_VALIDATOR_TAG_TYPE external)
+	/*@modifies internal@*/;
+
+/*@unused@*/
+static inline
+enum SH_VALIDATOR_TAG_TYPE
+tag_type_to_external (enum TAG_TYPE type)
+	/*@*/;
+
 static inline
 void
 init_tags (/*@special@*/ struct SH_Validator * validator)
@@ -106,6 +118,7 @@ static inline
 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@*/
@@ -126,6 +139,60 @@ remove_tag (struct SH_Validator * validator,
 	/*@modifies status@*/;
 
 
+static inline
+bool
+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
+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
 void
 init_tags (/*@special@*/ struct SH_Validator * validator)
@@ -201,6 +268,15 @@ init_tags_from_spec (/*@special@*/ struct SH_Validator * validator,
 		{
 			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)
 			{
@@ -233,6 +309,8 @@ copy_tag (/*@special@*/ struct tag_info * copy,
           /*@null@*/ /*@out@*/ struct SH_Status * status)
 	/*@defines copy->name@*/
 	/*@modifies copy->name@*/
+	/*@defines copy->type@*/
+	/*@modifies copy->type@*/
 	/*@globals fileSystem@*/
 	/*@modifies fileSystem@*/
 	/*@modifies status@*/
@@ -247,6 +325,7 @@ copy_tag (/*@special@*/ struct tag_info * copy,
 	}
 
 	copy->name = name;
+	copy->type = tag->type;
 	return TRUE;
 
 }
@@ -363,6 +442,7 @@ static inline
 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@*/
@@ -392,6 +472,8 @@ add_tag (struct SH_Validator * validator,
 		return FALSE;
 	}
 
+	tag_data.type = type;
+
 	/* allocate new space */
 	/* The addition and the multiplication is save,
 	 * because we have tested for this
@@ -509,6 +591,7 @@ 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@*/
@@ -518,15 +601,24 @@ SH_Validator_register_tag (struct SH_Validator * validator,
 	/*@modifies status@*/
 {
 	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 */
 	if (find_tag (validator, tag, &index))
 	{
+		/* update type */
+		validator->tags[index].type = type_internal;
 		set_success (status);
 		return TRUE;
 	}
 
-	return add_tag (validator, tag, index, status);
+	return add_tag (validator, tag, type_internal, index, status);
 }
 
 bool
diff --git a/src/lib/sefht/validator_tag.h b/src/lib/sefht/validator_tag.h
index 4d3d6ae..83b5c10 100644
--- a/src/lib/sefht/validator_tag.h
+++ b/src/lib/sefht/validator_tag.h
@@ -36,10 +36,23 @@
 #include "status.h"
 
 
+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,
+};
+
+
 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@*/
diff --git a/src/lib/sefht/validator_tag_data.h b/src/lib/sefht/validator_tag_data.h
index b8ea963..2809cd0 100644
--- a/src/lib/sefht/validator_tag_data.h
+++ b/src/lib/sefht/validator_tag_data.h
@@ -29,9 +29,20 @@
 #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                                                       \
diff --git a/tests/test_node_fragment.c b/tests/test_node_fragment.c
index d64afc4..70673f2 100644
--- a/tests/test_node_fragment.c
+++ b/tests/test_node_fragment.c
@@ -50,7 +50,8 @@ START_TEST(test_node_fragment_no_status)
 	data = SH_Data_new (NULL);
 	ck_assert_ptr_ne (NULL, data);
 
-	result = SH_Validator_register_tag (data->validator, tag, NULL);
+	result = SH_Validator_register_tag (data->validator, tag,
+	                                    SH_TAG_TYPE_NORMAL, NULL);
 	ck_assert_int_eq (TRUE, result);
 
 	/* test - invalid tag */
@@ -91,7 +92,8 @@ START_TEST(test_node_fragment_with_status)
 	data = SH_Data_new (NULL);
 	ck_assert_ptr_ne (NULL, data);
 
-	result = SH_Validator_register_tag (data->validator, tag, NULL);
+	result = SH_Validator_register_tag (data->validator, tag,
+	                                    SH_TAG_TYPE_NORMAL, NULL);
 	ck_assert_int_eq (TRUE, result);
 
 	/* test - invalid tag */
@@ -135,7 +137,8 @@ START_TEST(test_node_fragment_raw_no_status)
 	data = SH_Data_new (NULL);
 	ck_assert_ptr_ne (NULL, data);
 
-	result = SH_Validator_register_tag (data->validator, tag, NULL);
+	result = SH_Validator_register_tag (data->validator, tag,
+	                                    SH_TAG_TYPE_NORMAL, NULL);
 	ck_assert_int_eq (TRUE, result);
 
 	/* test - invalid tag */
@@ -177,7 +180,8 @@ START_TEST(test_node_fragment_raw_with_status)
 	data = SH_Data_new (NULL);
 	ck_assert_ptr_ne (NULL, data);
 
-	result = SH_Validator_register_tag (data->validator, tag, NULL);
+	result = SH_Validator_register_tag (data->validator, tag,
+	                                    SH_TAG_TYPE_NORMAL, NULL);
 	ck_assert_int_eq (TRUE, result);
 
 	/* test - invalid tag */
diff --git a/tests/test_validator_attr.c b/tests/test_validator_attr.c
index 752ef28..f189791 100644
--- a/tests/test_validator_attr.c
+++ b/tests/test_validator_attr.c
@@ -42,10 +42,10 @@
 #include "validator_html.h"
 
 const struct HTML_TAG_DEFINITION HTML_TAGS[] = {
-	{"html"},
-	{"aside"},
-	{"a"},
-	{"p"},
+	{"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[] = {
@@ -361,10 +361,11 @@ START_TEST(test_validator_attr_register_no_status)
 {
 	struct SH_Validator * validator;
 	const char * tag1 = "html";
-	const char * tag2 = "foobarbaz"; // mustn't be registered yet
+	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";
@@ -378,6 +379,19 @@ START_TEST(test_validator_attr_register_no_status)
 	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);
@@ -404,7 +418,7 @@ START_TEST(test_validator_attr_register_no_status)
 	ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name);
 	ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name);
 
-	/* test - automatic tag register */
+	/* test - register #2 */
 	result = SH_Validator_register_attr (validator, tag2, attr1,
 	                                     NULL);
 	ck_assert_int_eq (TRUE, result);
@@ -413,6 +427,15 @@ START_TEST(test_validator_attr_register_no_status)
 	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);
 
@@ -562,10 +585,11 @@ START_TEST(test_validator_attr_register_with_status)
 	struct SH_Status status;
 	struct SH_Validator * validator;
 	const char * tag1 = "html";
-	const char * tag2 = "foobarbaz"; // mustn't be registered yet
+	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";
@@ -579,6 +603,19 @@ START_TEST(test_validator_attr_register_with_status)
 	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,
@@ -609,7 +646,7 @@ START_TEST(test_validator_attr_register_with_status)
 	ck_assert_ptr_ne (tag1, validator->attrs[0].tags[0].name);
 	ck_assert_str_eq (tag1, validator->attrs[0].tags[0].name);
 
-	/* test - automatic tag register */
+	/* test - register #2 */
 	_status_preinit (status);
 	result = SH_Validator_register_attr (validator, tag2, attr1,
 	                                     &status);
@@ -620,6 +657,17 @@ START_TEST(test_validator_attr_register_with_status)
 	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);
 
@@ -797,6 +845,13 @@ START_TEST(test_validator_attr_deregister_no_status)
 	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);
@@ -1113,6 +1168,11 @@ START_TEST(test_validator_attr_deregister_no_status)
 	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);
@@ -1165,6 +1225,13 @@ START_TEST(test_validator_attr_deregister_with_status)
 	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);
@@ -1509,6 +1576,11 @@ START_TEST(test_validator_attr_deregister_with_status)
 	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);
@@ -1569,6 +1641,12 @@ START_TEST(test_validator_attr_check)
 	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);
diff --git a/tests/test_validator_tag.c b/tests/test_validator_tag.c
index aec3ec7..800c1a9 100644
--- a/tests/test_validator_tag.c
+++ b/tests/test_validator_tag.c
@@ -40,10 +40,10 @@
 /* override HTML spec */
 #include "validator_html.h"
 const struct HTML_TAG_DEFINITION HTML_TAGS[] = {
-	{"html"},
-	{"aside"},
-	{"html"},
-	{"body"}
+	{"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
 
@@ -99,10 +99,12 @@ START_TEST(test_validator_tag_copy_no_status)
 
 	#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
@@ -134,10 +136,12 @@ START_TEST(test_validator_tag_copy_with_status)
 
 	#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
@@ -160,12 +164,15 @@ START_TEST(test_validator_tag_spec_no_status)
 
 	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);
@@ -188,12 +195,15 @@ START_TEST(test_validator_tag_spec_with_status)
 
 	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);
@@ -211,34 +221,48 @@ START_TEST(test_validator_tag_register_no_status)
 	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, NULL);
+	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, NULL);
+	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, NULL);
+	result = SH_Validator_register_tag (validator, tag3, V, NULL);
 	ck_assert_int_eq (TRUE, result);
 
-	result = SH_Validator_register_tag (validator, tag4, NULL);
+	result = SH_Validator_register_tag (validator, tag4, V, NULL);
 	ck_assert_int_eq (TRUE, result);
 
-	result = SH_Validator_register_tag (validator, tag5, NULL);
+	result = SH_Validator_register_tag (validator, tag5, V, NULL);
 	ck_assert_int_eq (TRUE, result);
 
 	ck_assert_int_eq (4, validator->tag_n);
@@ -264,16 +288,18 @@ START_TEST(test_validator_tag_register_no_status)
 	{
 		sprintf (tagN, "tag%zu", validator->tag_n);
 	}
-	while (SH_Validator_register_tag (validator, tagN, NULL));
+	while (SH_Validator_register_tag (validator, tagN, V, NULL));
 
 	free (tagN);
 
 	/* test overflow */
-	result = SH_Validator_register_tag (validator, tag2, NULL);
+	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;
@@ -292,43 +318,61 @@ START_TEST(test_validator_tag_register_with_status)
 	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, &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, &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, &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, &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, &status);
+	result = SH_Validator_register_tag (validator, tag5, V, &status);
 	ck_assert_int_eq (TRUE, result);
 	ck_assert_int_eq (status.status, SUCCESS);
 
@@ -355,18 +399,20 @@ START_TEST(test_validator_tag_register_with_status)
 	{
 		sprintf (tagN, "tag%zu", validator->tag_n);
 	}
-	while (SH_Validator_register_tag (validator, tagN, NULL));
+	while (SH_Validator_register_tag (validator, tagN, V, NULL));
 
 	free (tagN);
 
 	/* test overflow */
 	_status_preinit (status);
-	result = SH_Validator_register_tag (validator, tag2, &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;
@@ -386,12 +432,14 @@ START_TEST(test_validator_tag_deregister_no_status)
 	validator = SH_Validator_new (NULL);
 	ck_assert_ptr_ne (NULL, validator);
 
-	result = SH_Validator_register_tag (validator, tag1, NULL);
+	#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, NULL);
+	result = SH_Validator_register_tag (validator, tag2, V, NULL);
 	ck_assert_int_eq (TRUE, result);
-	result = SH_Validator_register_tag (validator, tag3, NULL);
+	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);
@@ -458,12 +506,14 @@ START_TEST(test_validator_tag_deregister_with_status)
 	validator = SH_Validator_new (NULL);
 	ck_assert_ptr_ne (NULL, validator);
 
-	result = SH_Validator_register_tag (validator, tag1, NULL);
+	#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, NULL);
+	result = SH_Validator_register_tag (validator, tag2, V, NULL);
 	ck_assert_int_eq (TRUE, result);
-	result = SH_Validator_register_tag (validator, tag3, NULL);
+	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);
@@ -537,8 +587,10 @@ START_TEST(test_validator_tag_check)
 	validator = SH_Validator_new (NULL);
 	ck_assert_ptr_ne (NULL, validator);
 
-	result = SH_Validator_register_tag (validator, tag1, NULL);
+	#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);
diff --git a/todo.txt b/todo.txt
index f783128..c2bf520 100644
--- a/todo.txt
+++ b/todo.txt
@@ -18,6 +18,5 @@ Fragment:
 
 Validator:
 - check for global attributes
-- add tag types
 - fix cursed behaviour, when removing fails (remove_tag_for_all_attrs)
 - initialize from file
-- 
GitLab