diff --git a/sefht.geany b/sefht.geany
index 4d6615ed2b1d56c71743924940f2c86b98b7a488..5bbbe47b572d6466fc12a1e52737cec8033c56da 100644
--- a/sefht.geany
+++ b/sefht.geany
@@ -61,12 +61,12 @@ FILE_NAME_28=1867;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fp
 FILE_NAME_29=3036;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator.c;0;8
 FILE_NAME_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=1111;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=5802;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=13741;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=1148;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=1124;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=1818;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=1874;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=1099;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_35=15732;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=1682;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=1111;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
@@ -83,8 +83,8 @@ FILE_NAME_50=4221;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fp
 FILE_NAME_51=994;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text_mark.c;0;8
 FILE_NAME_52=2447;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8
 FILE_NAME_53=3880;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=13518;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=536;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftodo.txt;0;8
+FILE_NAME_54=29231;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=667;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/validator_attr.c b/src/lib/sefht/validator_attr.c
index d5f6218d63401268f1ff07a901bcfed127c99f85..5f42049fe7cd4f3a99a721085c33680d16670d3d 100644
--- a/src/lib/sefht/validator_attr.c
+++ b/src/lib/sefht/validator_attr.c
@@ -78,10 +78,18 @@ find_attr (const struct SH_Validator * validator,
            /*@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
 Attr
 add_attr (struct SH_Validator * validator,
           const char * attr,
+          /*@dependent@*/ const char * tag,
           size_t index,
           /*@null@*/ /*@out@*/ struct SH_Status * status)
 	/*@modifies validator->attrs@*/
@@ -122,7 +130,7 @@ get_attr_id_by_name (struct SH_Validator * validator,
 static inline
 bool
 remove_attr (struct SH_Validator * validator,
-             const char * attr,
+             const size_t index,
              /*@null@*/ /*@out@*/ struct SH_Status  * status)
 	/*@modifies validator->attrs@*/
 	/*@modifies validator->attr_n@*/
@@ -132,6 +140,40 @@ remove_attr (struct SH_Validator * validator,
 	/*@modifies status@*/;
 
 
+static inline
+bool
+attr_add_tag (struct attr_info * attr,
+              const size_t index,
+              /*@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@*/
+	/*@modifies validator->last_attr@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
 
 static inline
 bool
@@ -166,7 +208,9 @@ copy_attr (/*@special@*/ struct attr_info * copy,
 	/*@modifies fileSystem@*/
 	/*@modifies status@*/
 {
+	size_t index;
 	char * name;
+	struct attr_tag_info * tags;
 
 	name = strdup (attr->name);
 	if (NULL == name)
@@ -175,8 +219,22 @@ copy_attr (/*@special@*/ struct attr_info * copy,
 		return FALSE;
 	}
 
+	tags = malloc (sizeof (struct attr_tag_info) * attr->tag_n);
+	if (NULL == tags)
+	{
+		set_status (status, E_ALLOC, 3, "malloc failed");
+		free (name);
+		return FALSE;
+	}
+
 	copy->id = attr->id;
 	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;
 }
@@ -234,6 +292,7 @@ free_attr (struct attr_info attr)
 	/*@releases attr.name@*/
 {
 	free (attr.name);
+	free (attr.tags);
 	return;
 }
 
@@ -294,10 +353,49 @@ find_attr (const struct SH_Validator * validator,
 	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
 Attr
 add_attr (struct SH_Validator * validator,
           const char * attr,
+          /*@dependent@*/ const char * tag,
           size_t index,
           /*@null@*/ /*@out@*/ struct SH_Status * status)
 	/*@modifies validator->attrs@*/
@@ -331,6 +429,17 @@ add_attr (struct SH_Validator * validator,
 		return ATTR_ERR;
 	}
 
+	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 ATTR_ERR;
+	}
+
+	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
@@ -445,7 +554,7 @@ get_attr_id_by_name (struct SH_Validator * validator,
 static inline
 bool
 remove_attr (struct SH_Validator * validator,
-             const char * attr,
+             const size_t index,
              /*@null@*/ /*@out@*/ struct SH_Status  * status)
 	/*@modifies validator->attrs@*/
 	/*@modifies validator->attr_n@*/
@@ -456,16 +565,8 @@ remove_attr (struct SH_Validator * validator,
 {
 	struct attr_info attr_data;
 	struct attr_info * new_attrs;
-	size_t index;
 	size_t index2;
 
-	if (!find_attr (validator, attr, &index))
-	{
-		/* TODO: define whether this is an error */
-		set_status (status, E_VALUE, 3, "no such attr");
-		return FALSE;
-	}
-
 	/* preserve data as realloc may fail */
 	attr_data = validator->attrs[index];
 
@@ -502,16 +603,170 @@ remove_attr (struct SH_Validator * validator,
 	return TRUE;
 }
 
+static inline
+bool
+attr_add_tag (struct attr_info * attr,
+              const size_t index,
+              /*@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 ((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@*/
+	/*@modifies validator->last_attr@*/
+	/*@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;
+				}
+			}
+		}
+		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;
+}
+
 bool
 SH_Validator_check_attr (struct SH_Validator * validator,
+                         /*@null@*/ const char * tag,
                          const char * attr)
 	/*@*/
 {
-	return is_attr_name (validator, 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;
+
+		if (!find_attr_tag (attr, tag, &index)) return FALSE;
+	}
+
+	return TRUE;
 }
 
 Attr
 SH_Validator_register_attr (struct SH_Validator * validator,
+                            const char * tag,
                             const char * attr,
                             /*@null@*/ /*@out@*/
                             struct SH_Status * status)
@@ -523,22 +778,41 @@ SH_Validator_register_attr (struct SH_Validator * validator,
 	/*@modifies status@*/
 {
 	Attr attr_id;
+	/* note, that index is used for three different arrays */
 	size_t index;
 
+	if ((!find_tag (validator, tag, &index))
+	&& (!add_tag (validator, tag, index, status)))
+	{
+		return ATTR_ERR;
+	}
+
+	tag = validator->tags[index].name;
+
 	/* attr already registered */
 	attr_id = get_attr_id_by_name (validator, attr, &index);
 	if (attr_id != ATTR_ERR)
 	{
+		struct attr_info * attr;
+		attr = &validator->attrs[index];
+
+		if ((!find_attr_tag (attr, tag, &index))
+		&& (!attr_add_tag (attr, index, tag, status)))
+		{
+			return ATTR_ERR;
+		}
+
 		set_success (status);
 		return attr_id;
 	}
 
-	return add_attr (validator, attr, index, status);
+	return add_attr (validator, attr, tag, index, status);
 }
 
 bool
 SH_Validator_deregister_attr (struct SH_Validator * validator,
-                              const char * attr,
+                              /*@null@*/ const char * tag,
+                              /*@null@*/ const char * attr,
                               /*@null@*/ /*@out@*/
                               struct SH_Status * status)
 	/*@modifies validator->attr_n@*/
@@ -548,5 +822,75 @@ SH_Validator_deregister_attr (struct SH_Validator * validator,
 	/*@modifies fileSystem@*/
 	/*@modifies status@*/
 {
-	return remove_attr (validator, attr, 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 ATTR_ERR;
+		}
+
+		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 ATTR_ERR;
+			}
+
+			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 ATTR_ERR;
+		}
+
+		tag = validator->tags[index].name;
+
+		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
index 64ec2a5c974b2bc0953af00cbafa33b579c84a46..e17e12aee42b7db5794fe43f8d1077a46a16f8ac 100644
--- a/src/lib/sefht/validator_attr.h
+++ b/src/lib/sefht/validator_attr.h
@@ -43,6 +43,7 @@ typedef unsigned int Attr;
 
 Attr
 SH_Validator_register_attr (SH_Validator * validator,
+                            const char * tag,
                             const char * attr,
                             /*@null@*/ /*@out@*/
                             struct SH_Status * status)
@@ -53,7 +54,8 @@ SH_Validator_register_attr (SH_Validator * validator,
 
 bool
 SH_Validator_deregister_attr (SH_Validator * validator,
-                              const char * attr,
+                              /*@null@*/ const char * tag,
+                              /*@null@*/ const char * attr,
                               /*@null@*/ /*@out@*/
                               struct SH_Status * status)
 	/*@modifies validator@*/
@@ -63,6 +65,7 @@ SH_Validator_deregister_attr (SH_Validator * validator,
 
 bool
 SH_Validator_check_attr (struct SH_Validator * validator,
+                         /*@null@*/ const char * tag,
                          const char * attr)
 	/*@*/;
 
diff --git a/src/lib/sefht/validator_attr_data.h b/src/lib/sefht/validator_attr_data.h
index ff9d6cfd8d1dc9549eb0d1e287c7b8eed53ead40..858a1b2df4a64c70b8900351151ac8466c5ec639 100644
--- a/src/lib/sefht/validator_attr_data.h
+++ b/src/lib/sefht/validator_attr_data.h
@@ -29,10 +29,17 @@
 #error "Only <sefht/sefht.h> can be included directly."
 #endif
 
+struct attr_tag_info
+{
+	/*@relnull@*/ /*@dependent@*/ const char * name;
+};
+
 struct attr_info
 {
 	Attr id;
 	/*@only@*/ char * name;
+	size_t tag_n;
+	struct attr_tag_info * tags;
 };
 
 #define NEXT_ATTR(attr) (attr + 1)
diff --git a/src/lib/sefht/validator_tag.c b/src/lib/sefht/validator_tag.c
index ab7f28e9110f18cac524a957354462f5ca942190..9a24acf32f6600d6747f6629839df3d12bf123fa 100644
--- a/src/lib/sefht/validator_tag.c
+++ b/src/lib/sefht/validator_tag.c
@@ -562,6 +562,19 @@ get_tag_id_by_name (const struct SH_Validator * validator,
 	return validator->tags[*index].id;
 }
 
+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@*/
+	/*@modifies validator->last_attr@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
 static inline
 bool
 remove_tag (struct SH_Validator * validator, Tag id,
@@ -587,6 +600,12 @@ remove_tag (struct SH_Validator * validator, Tag id,
 		/* don't free name yet, as realloc may fail */
 		name = tags[index].name;
 
+		/* clear all references of this tag */
+		if (!remove_tag_for_all_attrs (validator, name, status))
+		{
+			return FALSE;
+		}
+
 		for (index2 = index; index2 < tag_n-1; index2++)
 		{
 			tags[index2] = tags[index2+1];
diff --git a/tests/test_validator_attr.c b/tests/test_validator_attr.c
index fdf49b412c8e3de79cfac84a5fed0f1c2c2e1aaa..f6cb44a43b7bd41b0cb9a9a251f33a29df78df7d 100644
--- a/tests/test_validator_attr.c
+++ b/tests/test_validator_attr.c
@@ -83,10 +83,10 @@ START_TEST(test_validator_copy_no_status)
 	validator = SH_Validator_new_html5 (NULL);
 	ck_assert_ptr_ne (NULL, validator);
 
-	attr = SH_Validator_register_attr (validator, "id", NULL);
+	attr = SH_Validator_register_attr (validator, "body", "id", NULL);
 	ck_assert_int_ne (ATTR_ERR, attr);
 
-	attr = SH_Validator_register_attr (validator, "lang", NULL);
+	attr = SH_Validator_register_attr (validator, "html", "lang", NULL);
 	ck_assert_int_ne (ATTR_ERR, attr);
 
 	/* test */
@@ -99,6 +99,7 @@ START_TEST(test_validator_copy_no_status)
 	ck_assert_int_eq (validator->last_attr, copy->last_attr);
 
 	#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);
 
@@ -106,8 +107,15 @@ START_TEST(test_validator_copy_no_status)
 	{
 		TEST_INT(attrs[index].id)
 		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)
+		}
 	}
 	#undef TEST_INT
+	#undef TEST_PTR
 	#undef TEST_STR
 
 	SH_Validator_free (copy);
@@ -126,10 +134,10 @@ START_TEST(test_validator_copy_with_status)
 	validator = SH_Validator_new_html5 (NULL);
 	ck_assert_ptr_ne (NULL, validator);
 
-	attr = SH_Validator_register_attr (validator, "id", NULL);
+	attr = SH_Validator_register_attr (validator, "body", "id", NULL);
 	ck_assert_int_ne (ATTR_ERR, attr);
 
-	attr = SH_Validator_register_attr (validator, "lang", NULL);
+	attr = SH_Validator_register_attr (validator, "html", "lang", NULL);
 	ck_assert_int_ne (ATTR_ERR, attr);
 
 	/* test */
@@ -144,6 +152,7 @@ START_TEST(test_validator_copy_with_status)
 	ck_assert_int_eq (validator->last_attr, copy->last_attr);
 
 	#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);
 
@@ -151,8 +160,15 @@ START_TEST(test_validator_copy_with_status)
 	{
 		TEST_INT(attrs[index].id)
 		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)
+		}
 	}
 	#undef TEST_INT
+	#undef TEST_PTR
 	#undef TEST_STR
 
 	SH_Validator_free (copy);
@@ -163,6 +179,11 @@ END_TEST
 START_TEST(test_validator_register_no_status)
 {
 	struct SH_Validator * validator;
+	const char * tag1 = "html";
+	const char * tag2 = "foobarbaz"; // mustn't be registered yet
+	const char * tag3 = "body";
+	const char * tag4 = "nav";
+	const char * tag5 = "aside";
 	const char * attr1 = "id";
 	const char * attr2 = "class";
 	const char * attr3 = "name";
@@ -177,33 +198,73 @@ START_TEST(test_validator_register_no_status)
 	ck_assert_ptr_ne (NULL, validator);
 
 	/* test - register */
-	attr = SH_Validator_register_attr (validator, attr1, NULL);
+	attr = SH_Validator_register_attr (validator, tag1, attr1, NULL);
 	ck_assert_int_eq (1, attr);
 
 	ck_assert_int_eq (validator->attr_n, 1);
 	ck_assert_int_eq (validator->last_attr, attr);
 
 	ck_assert_int_eq (validator->attrs[0].id, attr);
+	ck_assert_ptr_ne (validator->attrs[0].name, attr1);
 	ck_assert_str_eq (validator->attrs[0].name, attr1);
 
+	ck_assert_int_eq (validator->attrs[0].tag_n, 1);
+	ck_assert_ptr_ne (validator->attrs[0].tags[0].name, tag1);
+	ck_assert_str_eq (validator->attrs[0].tags[0].name, tag1);
+
 	/* test - duplicate registration */
-	attr_ = SH_Validator_register_attr (validator, attr1, NULL);
+	attr_ = SH_Validator_register_attr (validator, tag1, attr1, NULL);
 	ck_assert_int_eq (attr_, attr);
 
 	ck_assert_int_eq (validator->attr_n, 1);
 	ck_assert_int_eq (validator->last_attr, attr);
 
+	ck_assert_int_eq (validator->attrs[0].id, attr);
+	ck_assert_ptr_ne (validator->attrs[0].name, attr1);
+	ck_assert_str_eq (validator->attrs[0].name, attr1);
+
+	ck_assert_int_eq (validator->attrs[0].tag_n, 1);
+	ck_assert_ptr_ne (validator->attrs[0].tags[0].name, tag1);
+	ck_assert_str_eq (validator->attrs[0].tags[0].name, tag1);
+
+	/* test - automatic tag register */
+	attr = SH_Validator_register_attr (validator, tag2, attr1, NULL);
+	ck_assert_int_eq (1, attr);
+
+	ck_assert_int_eq (validator->attr_n, 1);
+	ck_assert_int_eq (validator->last_attr, attr);
+
 	ck_assert_int_eq (validator->attrs[0].id, attr);
 	ck_assert_str_eq (validator->attrs[0].name, attr1);
 
-	/* test - order */
-	attr = SH_Validator_register_attr (validator, attr3, NULL);
+	ck_assert_int_eq (validator->attrs[0].tag_n, 2);
+	/* 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])
+	{
+		ck_assert_ptr_ne (validator->attrs[0].tags[0].name, tag1);
+		ck_assert_str_eq (validator->attrs[0].tags[0].name, tag1);
+		ck_assert_ptr_ne (validator->attrs[0].tags[1].name, tag2);
+		ck_assert_str_eq (validator->attrs[0].tags[1].name, tag2);
+	}
+	else
+	{
+		ck_assert_ptr_ne (validator->attrs[0].tags[1].name, tag1);
+		ck_assert_str_eq (validator->attrs[0].tags[1].name, tag1);
+		ck_assert_ptr_ne (validator->attrs[0].tags[0].name, tag2);
+		ck_assert_str_eq (validator->attrs[0].tags[0].name, tag2);
+	}
+
+	/* test - order (attr) */
+	attr = SH_Validator_register_attr (validator, tag1, attr3, NULL);
 	ck_assert_int_eq (attr, 2);
 
-	attr = SH_Validator_register_attr (validator, attr4, NULL);
+	attr = SH_Validator_register_attr (validator, tag1, attr4, NULL);
 	ck_assert_int_eq (attr, 3);
 
-	attr = SH_Validator_register_attr (validator, attr5, NULL);
+	attr = SH_Validator_register_attr (validator, tag1, attr5, NULL);
 	ck_assert_int_eq (attr, 4);
 
 	ck_assert_int_eq (validator->attr_n, 4);
@@ -213,6 +274,27 @@ START_TEST(test_validator_register_no_status)
 	ck_assert_str_eq (validator->attrs[2].name, attr3);
 	ck_assert_str_eq (validator->attrs[3].name, attr4);
 
+	/* test - order (tag) */
+	attr = SH_Validator_register_attr (validator, tag2, attr4, NULL);
+	ck_assert_int_eq (attr, 3);
+
+	attr = SH_Validator_register_attr (validator, tag3, attr4, NULL);
+	ck_assert_int_eq (attr, 3);
+
+	attr = SH_Validator_register_attr (validator, tag4, attr4, NULL);
+	ck_assert_int_eq (attr, 3);
+
+	attr = SH_Validator_register_attr (validator, tag5, attr4, NULL);
+	ck_assert_int_eq (attr, 3);
+
+	ck_assert_int_eq (5, validator->attrs[3].tag_n);
+
+	for (size_t i = 0; i < validator->attrs[3].tag_n-1; i++)
+	{
+		ck_assert_int_lt ((size_t)validator->attrs[3].tags[i].name,
+		                  (size_t)validator->attrs[3].tags[i+1].name);
+	}
+
 	/* test - overflow detection */
 	/* make method fail by filling with garbage until
 	 * upper boundary is reached */
@@ -230,12 +312,12 @@ START_TEST(test_validator_register_no_status)
 	{
 		sprintf (attrN, "attr%zu", validator->attr_n);
 	}
-	while (ATTR_ERR != SH_Validator_register_attr (validator, attrN, NULL));
+	while (ATTR_ERR != SH_Validator_register_attr (validator, tag1, attrN, NULL));
 
 	free (attrN);
 
 	/* test overflow #1 */
-	attr = SH_Validator_register_attr (validator, attr2, NULL);
+	attr = SH_Validator_register_attr (validator, tag1, attr2, NULL);
 	ck_assert_int_eq (ATTR_ERR, attr);
 
 	ck_assert_int_eq (validator->attr_n, 10);
@@ -244,7 +326,7 @@ START_TEST(test_validator_register_no_status)
 	validator->attr_n = 1;
 	validator->last_attr = ATTR_MAX;
 
-	attr = SH_Validator_register_attr (validator, attr2, NULL);
+	attr = SH_Validator_register_attr (validator, tag1, attr2, NULL);
 	ck_assert_int_eq (ATTR_ERR, attr);
 
 	ck_assert_int_eq (validator->attr_n, 1);
@@ -260,6 +342,11 @@ START_TEST(test_validator_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 * tag3 = "body";
+	const char * tag4 = "nav";
+	const char * tag5 = "aside";
 	const char * attr1 = "id";
 	const char * attr2 = "class";
 	const char * attr3 = "name";
@@ -275,7 +362,7 @@ START_TEST(test_validator_register_with_status)
 
 	/* test - register */
 	_status_preinit (status);
-	attr = SH_Validator_register_attr (validator, attr1, &status);
+	attr = SH_Validator_register_attr (validator, tag1, attr1, &status);
 	ck_assert_int_eq (1, attr);
 	ck_assert_int_eq (SUCCESS, status.status);
 
@@ -283,33 +370,75 @@ START_TEST(test_validator_register_with_status)
 	ck_assert_int_eq (validator->last_attr, attr);
 
 	ck_assert_int_eq (validator->attrs[0].id, attr);
+	ck_assert_ptr_ne (validator->attrs[0].name, attr1);
 	ck_assert_str_eq (validator->attrs[0].name, attr1);
 
+	ck_assert_int_eq (validator->attrs[0].tag_n, 1);
+	ck_assert_ptr_ne (validator->attrs[0].tags[0].name, tag1);
+	ck_assert_str_eq (validator->attrs[0].tags[0].name, tag1);
+
 	/* test - duplicate registration */
 	_status_preinit (status);
-	attr_ = SH_Validator_register_attr (validator, attr1, &status);
+	attr_ = SH_Validator_register_attr (validator, tag1, attr1, &status);
 	ck_assert_int_eq (attr_, attr);
 	ck_assert_int_eq (SUCCESS, status.status);
 
 	ck_assert_int_eq (validator->attr_n, 1);
 	ck_assert_int_eq (validator->last_attr, attr);
 
+	ck_assert_int_eq (validator->attrs[0].id, attr);
+	ck_assert_ptr_ne (validator->attrs[0].name, attr1);
+	ck_assert_str_eq (validator->attrs[0].name, attr1);
+
+	ck_assert_int_eq (validator->attrs[0].tag_n, 1);
+	ck_assert_ptr_ne (validator->attrs[0].tags[0].name, tag1);
+	ck_assert_str_eq (validator->attrs[0].tags[0].name, tag1);
+
+	/* test - automatic tag register */
+	_status_preinit (status);
+	attr = SH_Validator_register_attr (validator, tag2, attr1, &status);
+	ck_assert_int_eq (1, attr);
+	ck_assert_int_eq (SUCCESS, status.status);
+
+	ck_assert_int_eq (validator->attr_n, 1);
+	ck_assert_int_eq (validator->last_attr, attr);
+
 	ck_assert_int_eq (validator->attrs[0].id, attr);
 	ck_assert_str_eq (validator->attrs[0].name, attr1);
 
-	/* test - order */
+	ck_assert_int_eq (validator->attrs[0].tag_n, 2);
+	/* 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])
+	{
+		ck_assert_ptr_ne (validator->attrs[0].tags[0].name, tag1);
+		ck_assert_str_eq (validator->attrs[0].tags[0].name, tag1);
+		ck_assert_ptr_ne (validator->attrs[0].tags[1].name, tag2);
+		ck_assert_str_eq (validator->attrs[0].tags[1].name, tag2);
+	}
+	else
+	{
+		ck_assert_ptr_ne (validator->attrs[0].tags[1].name, tag1);
+		ck_assert_str_eq (validator->attrs[0].tags[1].name, tag1);
+		ck_assert_ptr_ne (validator->attrs[0].tags[0].name, tag2);
+		ck_assert_str_eq (validator->attrs[0].tags[0].name, tag2);
+	}
+
+	/* test - order (attr) */
 	_status_preinit (status);
-	attr = SH_Validator_register_attr (validator, attr3, &status);
+	attr = SH_Validator_register_attr (validator, tag1, attr3, &status);
 	ck_assert_int_eq (attr, 2);
 	ck_assert_int_eq (status.status, SUCCESS);
 
 	_status_preinit (status);
-	attr = SH_Validator_register_attr (validator, attr4, &status);
+	attr = SH_Validator_register_attr (validator, tag1, attr4, &status);
 	ck_assert_int_eq (attr, 3);
 	ck_assert_int_eq (status.status, SUCCESS);
 
 	_status_preinit (status);
-	attr = SH_Validator_register_attr (validator, attr5, &status);
+	attr = SH_Validator_register_attr (validator, tag1, attr5, &status);
 	ck_assert_int_eq (attr, 4);
 	ck_assert_int_eq (status.status, SUCCESS);
 
@@ -320,6 +449,35 @@ START_TEST(test_validator_register_with_status)
 	ck_assert_str_eq (validator->attrs[2].name, attr3);
 	ck_assert_str_eq (validator->attrs[3].name, attr4);
 
+	/* test - order (tag) */
+	_status_preinit (status);
+	attr = SH_Validator_register_attr (validator, tag2, attr4, &status);
+	ck_assert_int_eq (attr, 3);
+	ck_assert_int_eq (status.status, SUCCESS);
+
+	_status_preinit (status);
+	attr = SH_Validator_register_attr (validator, tag3, attr4, &status);
+	ck_assert_int_eq (attr, 3);
+	ck_assert_int_eq (status.status, SUCCESS);
+
+	_status_preinit (status);
+	attr = SH_Validator_register_attr (validator, tag4, attr4, &status);
+	ck_assert_int_eq (attr, 3);
+	ck_assert_int_eq (status.status, SUCCESS);
+
+	_status_preinit (status);
+	attr = SH_Validator_register_attr (validator, tag5, attr4, &status);
+	ck_assert_int_eq (attr, 3);
+	ck_assert_int_eq (status.status, SUCCESS);
+
+	ck_assert_int_eq (5, validator->attrs[3].tag_n);
+
+	for (size_t i = 0; i < validator->attrs[3].tag_n-1; i++)
+	{
+		ck_assert_int_lt ((size_t)validator->attrs[3].tags[i].name,
+		                  (size_t)validator->attrs[3].tags[i+1].name);
+	}
+
 	/* test - overflow detection */
 	/* make method fail by filling with garbage until
 	 * upper boundary is reached */
@@ -337,13 +495,13 @@ START_TEST(test_validator_register_with_status)
 	{
 		sprintf (attrN, "attr%zu", validator->attr_n);
 	}
-	while (ATTR_ERR != SH_Validator_register_attr (validator, attrN, NULL));
+	while (ATTR_ERR != SH_Validator_register_attr (validator, tag1, attrN, NULL));
 
 	free (attrN);
 
 	/* test overflow #1 */
 	_status_preinit (status);
-	attr = SH_Validator_register_attr (validator, attr2, &status);
+	attr = SH_Validator_register_attr (validator, tag1, attr2, &status);
 	ck_assert_int_eq (ATTR_ERR, attr);
 	ck_assert_int_eq (E_DOMAIN, status.status);
 
@@ -354,7 +512,7 @@ START_TEST(test_validator_register_with_status)
 	validator->last_attr = ATTR_MAX;
 
 	_status_preinit (status);
-	attr = SH_Validator_register_attr (validator, attr2, &status);
+	attr = SH_Validator_register_attr (validator, tag1, attr2, &status);
 	ck_assert_int_eq (ATTR_ERR, attr);
 	ck_assert_int_eq (E_DOMAIN, status.status);
 
@@ -377,27 +535,156 @@ START_TEST(test_validator_deregister_no_status)
 	validator = SH_Validator_new (NULL);
 	ck_assert_ptr_ne (NULL, validator);
 
-	attr = SH_Validator_register_attr (validator, "attr", NULL);
+	attr = SH_Validator_register_attr (validator, "html", "attr", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "html", "id", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "html", "name", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "html", "class", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "body", "attr", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "body", "id", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "body", "name", NULL);
 	ck_assert_int_ne (ATTR_ERR, attr);
-	attr = SH_Validator_register_attr (validator, "id", NULL);
+	attr = SH_Validator_register_attr (validator, "body", "class", NULL);
 	ck_assert_int_ne (ATTR_ERR, attr);
 
 	/* test - consistency */
-	ck_assert_int_eq (2, validator->attr_n);
+	ck_assert_int_eq (4, validator->attr_n);
 	ck_assert_str_eq ("attr", validator->attrs[0].name);
-	ck_assert_str_eq ("id", validator->attrs[1].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_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);
+
+	/* 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])
+	{
+		ck_assert_str_eq ("html", validator->attrs[0].tags[0].name);
+		ck_assert_str_eq ("html", validator->attrs[1].tags[0].name);
+		ck_assert_str_eq ("html", validator->attrs[2].tags[0].name);
+		ck_assert_str_eq ("html", validator->attrs[3].tags[0].name);
+		ck_assert_str_eq ("body", validator->attrs[0].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[1].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[2].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[3].tags[1].name);
+	}
+	else
+	{
+		ck_assert_str_eq ("html", validator->attrs[0].tags[1].name);
+		ck_assert_str_eq ("html", validator->attrs[1].tags[1].name);
+		ck_assert_str_eq ("html", validator->attrs[2].tags[1].name);
+		ck_assert_str_eq ("html", validator->attrs[3].tags[1].name);
+		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);
+		ck_assert_str_eq ("body", validator->attrs[3].tags[0].name);
+	}
+
 
 	/* test - existent attr */
-	result = SH_Validator_deregister_attr (validator, "attr", NULL);
+	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 (3, 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_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);
+
+
 	/* test - non existent attr */
-	result = SH_Validator_deregister_attr (validator, "attr", NULL);
+	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 (1, validator->attr_n);
-	ck_assert_str_eq ("id", validator->attrs[0].name);
+	ck_assert_int_eq (2, validator->attr_n);
+	ck_assert_str_eq ("class", validator->attrs[0].name);
+	ck_assert_str_eq ("id", validator->attrs[1].name);
+	ck_assert_int_eq (2, validator->attrs[0].tag_n);
+	ck_assert_int_eq (2, validator->attrs[1].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])
+	{
+		ck_assert_str_eq ("html", validator->attrs[0].tags[0].name);
+		ck_assert_str_eq ("html", validator->attrs[1].tags[0].name);
+		ck_assert_str_eq ("body", validator->attrs[0].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[1].tags[1].name);
+	}
+	else
+	{
+		ck_assert_str_eq ("html", validator->attrs[0].tags[1].name);
+		ck_assert_str_eq ("html", validator->attrs[1].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[0].tags[0].name);
+		ck_assert_str_eq ("body", validator->attrs[1].tags[0].name);
+	}
+
+
+	/* 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 (2, validator->attr_n);
+	ck_assert_str_eq ("class", validator->attrs[0].name);
+	ck_assert_str_eq ("id", validator->attrs[1].name);
+	ck_assert_int_eq (1, validator->attrs[0].tag_n);
+	ck_assert_int_eq (1, validator->attrs[1].tag_n);
+	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);
@@ -415,33 +702,174 @@ START_TEST(test_validator_deregister_with_status)
 	validator = SH_Validator_new (NULL);
 	ck_assert_ptr_ne (NULL, validator);
 
-	attr = SH_Validator_register_attr (validator, "attr", NULL);
+	attr = SH_Validator_register_attr (validator, "html", "attr", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "html", "id", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "html", "name", NULL);
 	ck_assert_int_ne (ATTR_ERR, attr);
-	attr = SH_Validator_register_attr (validator, "id", NULL);
+	attr = SH_Validator_register_attr (validator, "html", "class", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "body", "attr", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "body", "id", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "body", "name", NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, "body", "class", NULL);
 	ck_assert_int_ne (ATTR_ERR, attr);
 
 	/* test - consistency */
-	ck_assert_int_eq (2, validator->attr_n);
+	ck_assert_int_eq (4, validator->attr_n);
 	ck_assert_str_eq ("attr", validator->attrs[0].name);
-	ck_assert_str_eq ("id", validator->attrs[1].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_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);
+
+	/* 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])
+	{
+		ck_assert_str_eq ("html", validator->attrs[0].tags[0].name);
+		ck_assert_str_eq ("html", validator->attrs[1].tags[0].name);
+		ck_assert_str_eq ("html", validator->attrs[2].tags[0].name);
+		ck_assert_str_eq ("html", validator->attrs[3].tags[0].name);
+		ck_assert_str_eq ("body", validator->attrs[0].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[1].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[2].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[3].tags[1].name);
+	}
+	else
+	{
+		ck_assert_str_eq ("html", validator->attrs[0].tags[1].name);
+		ck_assert_str_eq ("html", validator->attrs[1].tags[1].name);
+		ck_assert_str_eq ("html", validator->attrs[2].tags[1].name);
+		ck_assert_str_eq ("html", validator->attrs[3].tags[1].name);
+		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);
+		ck_assert_str_eq ("body", validator->attrs[3].tags[0].name);
+	}
+
 
 	/* test - existent attr */
 	_status_preinit (status);
-	result = SH_Validator_deregister_attr (validator, "attr",
+	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 (3, 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_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);
+
+
 	/* test - non existent attr */
 	_status_preinit (status);
-	result = SH_Validator_deregister_attr (validator, "attr",
+	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 (1, validator->attr_n);
-	ck_assert_str_eq ("id", validator->attrs[0].name);
+	ck_assert_int_eq (2, validator->attr_n);
+	ck_assert_str_eq ("class", validator->attrs[0].name);
+	ck_assert_str_eq ("id", validator->attrs[1].name);
+	ck_assert_int_eq (2, validator->attrs[0].tag_n);
+	ck_assert_int_eq (2, validator->attrs[1].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])
+	{
+		ck_assert_str_eq ("html", validator->attrs[0].tags[0].name);
+		ck_assert_str_eq ("html", validator->attrs[1].tags[0].name);
+		ck_assert_str_eq ("body", validator->attrs[0].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[1].tags[1].name);
+	}
+	else
+	{
+		ck_assert_str_eq ("html", validator->attrs[0].tags[1].name);
+		ck_assert_str_eq ("html", validator->attrs[1].tags[1].name);
+		ck_assert_str_eq ("body", validator->attrs[0].tags[0].name);
+		ck_assert_str_eq ("body", validator->attrs[1].tags[0].name);
+	}
+
+
+	/* 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 (2, validator->attr_n);
+	ck_assert_str_eq ("class", validator->attrs[0].name);
+	ck_assert_str_eq ("id", validator->attrs[1].name);
+	ck_assert_int_eq (1, validator->attrs[0].tag_n);
+	ck_assert_int_eq (1, validator->attrs[1].tag_n);
+	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);
@@ -451,9 +879,14 @@ END_TEST
 START_TEST(test_validator_check)
 {
 	struct SH_Validator * validator;
+	const char * tag1 = "html";
+	const char * tag2 = "html";
+	const char * tag3 = "head";
+	const char * tag4 = "body";
 	const char * attr1  = "id";
 	const char * attr2  = "id";
 	const char * attr3  = "class";
+	const char * attr4  = "name";
 	Tag attr;
 	bool result;
 
@@ -461,17 +894,34 @@ START_TEST(test_validator_check)
 	validator = SH_Validator_new (NULL);
 	ck_assert_ptr_ne (NULL, validator);
 
-	attr = SH_Validator_register_attr (validator, attr1, NULL);
+	attr = SH_Validator_register_attr (validator, tag1, attr1, NULL);
+	ck_assert_int_ne (ATTR_ERR, attr);
+	attr = SH_Validator_register_attr (validator, tag3, attr3, NULL);
 	ck_assert_int_ne (ATTR_ERR, attr);
 
 	/* test */
-	result = SH_Validator_check_attr (validator, attr1);
+	result = SH_Validator_check_attr (validator, tag1, attr1);
+	ck_assert_int_eq (TRUE, result);
+
+	result = SH_Validator_check_attr (validator, tag2, attr2);
 	ck_assert_int_eq (TRUE, result);
 
-	result = SH_Validator_check_attr (validator, attr2);
+	result = SH_Validator_check_attr (validator, tag1, attr3);
+	ck_assert_int_eq (FALSE, result);
+
+	result = SH_Validator_check_attr (validator, tag3, attr4);
+	ck_assert_int_eq (FALSE, result);
+
+	result = SH_Validator_check_attr (validator, tag4, attr2);
+	ck_assert_int_eq (FALSE, result);
+
+	result = SH_Validator_check_attr (validator, tag4, attr4);
+	ck_assert_int_eq (FALSE, result);
+
+	result = SH_Validator_check_attr (validator, NULL, attr2);
 	ck_assert_int_eq (TRUE, result);
 
-	result = SH_Validator_check_attr (validator, attr3);
+	result = SH_Validator_check_attr (validator, NULL, attr4);
 	ck_assert_int_eq (FALSE, result);
 
 	/* cleanup */
diff --git a/todo.txt b/todo.txt
index 0de5b07d491a360e2d6eeda4d7085fd3463d95a4..3c9b4df92fb45ea9bfa7d898c5791f2863890eb2 100644
--- a/todo.txt
+++ b/todo.txt
@@ -20,3 +20,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:
+- bugfix: remove_attr: failure recovery, fix move back loop
+- remove unnecessary ids
+- tests missing for deregister_tag