diff --git a/configure.ac b/configure.ac
index a9bd36d47f78316399d1bd155314fcd8fac03b01..13e1ff8571638bd727366243fb3022b6a6f27625 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,16 +12,20 @@ AM_INIT_AUTOMAKE([foreign -Wall -Werror subdir-objects])
 
 # Checks for programs.
 AC_PROG_CC
+AM_PROG_AR
+AC_PROG_LIBTOOL
 
 PKG_CHECK_MODULES([CHECK], [check >= 0.9.4])
 
 # Checks for libraries.
+LT_INIT()
 
 # Checks for header files.
 AC_CHECK_HEADER([errno.h])
 AC_CHECK_HEADER([limits.h])
 AC_CHECK_HEADER([math.h])
 AC_CHECK_HEADER([stdbool.h])
+AC_CHECK_HEADER([stdint.h])
 AC_CHECK_HEADER([stdio.h])
 AC_CHECK_HEADER([stdlib.h])
 AC_CHECK_HEADER([string.h])
@@ -33,6 +37,7 @@ AC_CHECK_HEADER([string.h])
 # Makefiles
 AC_CONFIG_FILES([Makefile
 		 src/Makefile
+		 src/lib/Makefile
 		 tests/Makefile])
 
 AC_OUTPUT
diff --git a/sefht.geany b/sefht.geany
index c890b137309778bf4945c7251121c6832203046e..4f9cbf8e4e8412fdce22ae688ed0cfa175a51571 100644
--- a/sefht.geany
+++ b/sefht.geany
@@ -28,36 +28,41 @@ long_line_behaviour=1
 long_line_column=72
 
 [files]
-current_page=22
-FILE_NAME_0=607;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fconfigure.ac;0;8
-FILE_NAME_1=384;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2FMakefile.am;0;8
-FILE_NAME_2=2517;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fmain.c;0;8
-FILE_NAME_3=1280;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fcms.c;0;8
-FILE_NAME_4=1148;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fcms.h;0;8
-FILE_NAME_5=4036;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fdata.c;0;8
-FILE_NAME_6=1099;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fdata.h;0;8
-FILE_NAME_7=1500;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Ffragment.c;0;8
-FILE_NAME_8=1058;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Ffragment.h;0;8
-FILE_NAME_9=1420;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fnode_fragment.c;0;8
-FILE_NAME_10=1574;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fnode_fragment.h;0;8
-FILE_NAME_11=1528;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Ftext.c;0;8
-FILE_NAME_12=1397;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Ftext.h;0;8
-FILE_NAME_13=1351;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fvalidator.c;0;8
-FILE_NAME_14=1130;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fvalidator.h;0;8
-FILE_NAME_15=994;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Ferror.h;0;4
-FILE_NAME_16=881;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flog.h;0;4
-FILE_NAME_17=938;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fmacro.h;0;8
-FILE_NAME_18=847;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fsefht.h;0;8
-FILE_NAME_19=1089;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2FMakefile.am;0;8
-FILE_NAME_20=1113;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_cms.c;0;8
-FILE_NAME_21=3059;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_data.c;0;8
-FILE_NAME_22=8535;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_node_fragment.c;0;8
-FILE_NAME_23=5331;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text.c;0;8
-FILE_NAME_24=6057;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8
-FILE_NAME_25=55;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftodo.txt;0;8
+current_page=21
+FILE_NAME_0=923;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fconfigure.ac;0;8
+FILE_NAME_1=73;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2FMakefile.am;0;8
+FILE_NAME_2=1143;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Fmain.c;0;8
+FILE_NAME_3=1260;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2FMakefile.am;0;8
+FILE_NAME_4=1593;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fcms.c;0;8
+FILE_NAME_5=901;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fcms.h;0;8
+FILE_NAME_6=2921;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fdata.c;0;8
+FILE_NAME_7=904;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=1185;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ffragment.c;0;8
+FILE_NAME_9=916;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ffragment.h;0;8
+FILE_NAME_10=1975;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_11=2173;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_12=8225;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_13=931;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_14=25820;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.c;0;8
+FILE_NAME_15=904;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.h;0;8
+FILE_NAME_16=1779;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator.c;0;8
+FILE_NAME_17=919;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fvalidator.h;0;8
+FILE_NAME_18=14237;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_19=859;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_20=4608;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fstatus.h;0;8
+FILE_NAME_21=901;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Flog.h;0;4
+FILE_NAME_22=907;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fmacro.h;0;8
+FILE_NAME_23=1078;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fsefht.h;0;8
+FILE_NAME_24=290;Make;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2FMakefile.am;0;8
+FILE_NAME_25=1085;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_cms.c;0;8
+FILE_NAME_26=3283;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_data.c;0;8
+FILE_NAME_27=13617;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_node_fragment.c;0;8
+FILE_NAME_28=11068;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text.c;0;8
+FILE_NAME_29=5744;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8
+FILE_NAME_30=165;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftodo.txt;0;8
 
 [VTE]
-last_dir=/home/jonathan/Dokumente/projekte/prgm/internet/web/SeFHT/tests
+last_dir=/home/jonathan/Documents/projects/prgm/internet/web/SeFHT/tests
 
 [prjorg]
 source_patterns=*.c;*.C;*.cpp;*.cxx;*.c++;*.cc;*.m;
diff --git a/src/Makefile.am b/src/Makefile.am
index f720509f6f670fa580e327d27fa41fec0f7468ef..211dd58bd9a275df055225ff186f97962d4a016e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,20 +1,13 @@
 ## Process this file with automake to produce Makefile.in
 
+SUBDIRS = lib
+
 AM_CFLAGS = -Wall -Wextra
 
-bin_PROGRAMS = sefht.fcgi
+bin_PROGRAMS = sefht_demo
 
-sefht_fcgi_SOURCES =
-sefht_fcgi_SOURCES += main.c
-sefht_fcgi_SOURCES += macro.h
-sefht_fcgi_SOURCES += sefht.h
-sefht_fcgi_SOURCES += error.h
-sefht_fcgi_SOURCES += log.h
-sefht_fcgi_SOURCES += cms.c cms.h
-sefht_fcgi_SOURCES += data.c data.h
-sefht_fcgi_SOURCES += fragment.c fragment.h
-sefht_fcgi_SOURCES += node_fragment.c node_fragment.h
-sefht_fcgi_SOURCES += text.c text.h
-sefht_fcgi_SOURCES += validator.c validator.h
+sefht_demo_SOURCES =
+sefht_demo_SOURCES += main.c
 
-sefht_fcgi_LDADD = -lm
+sefht_demo_CPPFLAGS = -I$(srcdir)/lib
+sefht_demo_LDADD = -lm lib/libsefht.la
diff --git a/src/data.c b/src/data.c
deleted file mode 100644
index 2dd0bddcc2ab09c4e663d261ba257d4232075e9c..0000000000000000000000000000000000000000
--- a/src/data.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * data.c
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 <errno.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "macro.h"
-#include "error.h"
-#include "log.h"
-
-#include "validator.h"
-
-#include "data.h"
-
-
-
-static inline bool init_validator (struct SH_Data * data,
-				   struct SH_Error * error);
-static inline void init_pages (struct SH_Data * data);
-
-struct SH_Data *
-SH_Data_new (struct SH_Error * error)
-{
-	struct SH_Data * data;
-	data = malloc (sizeof (struct SH_Data));
-
-	if (data == NULL)
-	{
-		ERROR1 ("Memory allocation for SH_Data failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return NULL;
-	}
-
-	if (!init_validator (data, error))
-	{
-		free (data);
-
-		return NULL;
-	}
-
-	init_pages (data);
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return data;
-}
-
-static inline void free_validator (struct SH_Data * data);
-static inline void free_pages (struct SH_Data * data);
-
-void
-SH_Data_free (struct SH_Data * data, struct SH_Error * error)
-{
-	free_validator (data);
-	free_pages (data);
-
-	free (data);
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return;
-}
-
-static inline bool
-init_validator (struct SH_Data * data, struct SH_Error * error)
-{
-	data->validator = SH_Validator_new (error);
-
-	if (data->validator == NULL)
-	{
-		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;
-}
-
-static inline void
-free_validator (struct SH_Data * data)
-{
-	SH_Validator_free (data->validator, NULL);
-	return;
-}
-
-static inline void
-init_pages (struct SH_Data * data)
-{
-	data->page_n = 0;
-	data->pages = malloc (0);
-	data->last_page = PAGE_ERR;
-
-	return;
-}
-
-static inline void
-free_pages (struct SH_Data * data)
-{
-	unsigned int index;
-
-	for (index = 0; index < data->page_n; index++)
-	{
-		free (data->pages[index].name);
-	}
-
-	free (data->pages);
-	return;
-}
-
-page_t
-SH_Data_register_page (struct SH_Data * data, const char * name,
-		       struct SH_Error * error)
-{
-	/* abort on overflow */
-	if (data->page_n == UINT_MAX || data->last_page == PAGE_MAX)
-	{
-		ERROR1 ("Maximum number of pages reached.\n");
-
-		if (error != NULL)
-		{
-			error->type = DOMAIN_ERROR;
-		}
-
-		return PAGE_ERR;
-	}
-
-	data->pages = realloc (data->pages,
-			(data->page_n + 1) * sizeof (struct SH_Page));
-
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for page data failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return PAGE_ERR;
-	}
-
-	NEXT_PAGE(data->last_page);
-
-	data->pages[data->page_n].id = data->last_page;
-	data->pages[data->page_n].name = strdup (name);
-
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for page data failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return PAGE_ERR;
-	}
-
-	data->page_n++;
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return data->last_page;
-}
diff --git a/src/error.h b/src/error.h
deleted file mode 100644
index f8e2e5ec2b31d79116ea91949ffe1780f7c0f1a1..0000000000000000000000000000000000000000
--- a/src/error.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * error.h
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 _ERROR_H
-#define _ERROR_H
-
-
-enum SH_ErrorType
-{
-	UNDEFINED,
-	ALLOCATION_FAILED,
-	DOMAIN_ERROR,
-	VALUE_ERROR,
-	SUCCESS
-};
-
-struct SH_Error
-{
-	enum SH_ErrorType type;
-};
-
-#endif /* _ERROR_H */
diff --git a/src/fragment.c b/src/fragment.c
deleted file mode 100644
index 8fecbd22100d57b256f97d96b0191a51f49f47a2..0000000000000000000000000000000000000000
--- a/src/fragment.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * fragment.c
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 "macro.h"
-#include "error.h"
-#include "log.h"
-
-#include "text.h"
-
-#include "fragment.h"
-
-
-struct SH_Fragment *
-SH_Fragment_copy (struct SH_Fragment * fragment,
-		  struct SH_Error * error)
-{
-	return fragment->methods->copy (fragment, error);
-}
-
-void
-SH_Fragment_free (struct SH_Fragment * fragment,
-		  struct SH_Error * error)
-{
-	return fragment->methods->free (fragment, error);
-}
-
-struct SH_Text *
-SH_Fragment_to_html (struct SH_Fragment * fragment,
-		     enum HTML_MODE mode,
-		     unsigned int indent_base,
-		     unsigned int indent_step,
-		     char * indent_char,
-		     struct SH_Error * error)
-{
-	return fragment->methods->to_html (fragment, mode,
-					   indent_base,
-					   indent_step,
-					   indent_char,
-					   error);
-}
diff --git a/src/fragment.h b/src/fragment.h
deleted file mode 100644
index c15ed290142b7fa58697f4d6689019c12b4a98a6..0000000000000000000000000000000000000000
--- a/src/fragment.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * fragment.h
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 _FRAGMENT_H
-#define _FRAGMENT_H
-
-#include "data.h"
-#include "text.h"
-
-
-enum HTML_MODE
-{
-	INLINE,
-	WRAP
-};
-
-enum SH_FRAGMENT_TYPE
-{
-	NODE
-};
-
-struct fragment_methods;
-
-struct SH_Fragment;
-struct SH_Fragment
-{
-	struct SH_Data * data;
-
-	enum SH_FRAGMENT_TYPE type;
-	const struct fragment_methods * methods;
-};
-
-struct fragment_methods
-{
-	struct SH_Fragment * (*copy) (struct SH_Fragment *,
-				      struct SH_Error *);
-
-	void (*free) (struct SH_Fragment *,
-		      struct SH_Error *);
-
-	struct SH_Text * (*to_html) (struct SH_Fragment *,
-				     enum HTML_MODE,
-				     unsigned int,
-				     unsigned int,
-				     const char *,
-				     struct SH_Error *);
-};
-
-
-static inline
-void
-init_fragment (struct SH_Fragment * fragment,
-	       struct SH_Data * data,
-	       const enum SH_FRAGMENT_TYPE type,
-	       const struct fragment_methods * const methods)
-{
-	fragment->data = data;
-	fragment->type = type;
-	fragment->methods = methods;
-
-	return;
-}
-
-static inline
-void
-free_fragment (struct SH_Fragment * fragment)
-{
-	return;
-}
-
-static inline
-void
-copy_fragment (struct SH_Fragment * copy,
-	       struct SH_Fragment * fragment)
-{
-	copy->data = fragment->data;
-	copy->type = fragment->type;
-	copy->methods = fragment->methods;
-
-	return;
-}
-
-static inline
-enum SH_FRAGMENT_TYPE
-get_type (struct SH_Fragment * fragment)
-{
-	return fragment->type;
-}
-
-
-struct SH_Fragment *
-SH_Fragment_copy (struct SH_Fragment * fragment,
-		  struct SH_Error * error);
-
-void
-SH_Fragment_free (struct SH_Fragment * fragment,
-		  struct SH_Error * error);
-
-struct SH_Text *
-SH_Fragment_to_html (struct SH_Fragment * fragment,
-		     enum HTML_MODE mode,
-		     unsigned int indent_base,
-		     unsigned int indent_step,
-		     char * indent_char,
-		     struct SH_Error * error);
-
-#endif /* _FRAGMENT_H */
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..38a43da3fcc74c31cb77038637ad7ba63fcdee93
--- /dev/null
+++ b/src/lib/Makefile.am
@@ -0,0 +1,38 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CFLAGS = -Wall -Wextra -Wno-nonnull
+
+lib_LTLIBRARIES = libsefht.la
+
+libsefht_la_SOURCES =
+libsefht_la_SOURCES += sefht/sefht.h
+libsefht_la_SOURCES += sefht/macro.h
+libsefht_la_SOURCES += sefht/log.h
+libsefht_la_SOURCES += sefht/status.h
+libsefht_la_SOURCES += sefht/cms.c sefht/cms.h
+libsefht_la_SOURCES += sefht/data.c sefht/data.h
+libsefht_la_SOURCES += sefht/fragment.c sefht/fragment.h
+libsefht_la_SOURCES += sefht/node_fragment.c sefht/node_fragment.h
+libsefht_la_SOURCES += sefht/text.c sefht/text.h
+libsefht_la_SOURCES += sefht/validator.c sefht/validator.h
+
+EXTRA_DIST =
+EXTRA_DIST += sefht/fragment_class.c
+EXTRA_DIST += sefht/fragment_data.c
+EXTRA_DIST += sefht/validator_tag.c
+
+nobase_include_HEADERS =
+nobase_include_HEADERS += sefht/sefht.h
+nobase_include_HEADERS += sefht/macro.h
+nobase_include_HEADERS += sefht/log.h
+nobase_include_HEADERS += sefht/status.h
+nobase_include_HEADERS += sefht/cms.h
+nobase_include_HEADERS += sefht/data.h
+nobase_include_HEADERS += sefht/fragment.h
+nobase_include_HEADERS += sefht/node_fragment.h
+nobase_include_HEADERS += sefht/text.h
+nobase_include_HEADERS += sefht/validator.h
+nobase_include_HEADERS += sefht/validator_tag.h
+
+libsefht_la_CPPFLAGS = -DSEFHT_COMPILATION
+libsefht_la_LDFLAGS = -version_info 0:0:0
diff --git a/src/cms.c b/src/lib/sefht/cms.c
similarity index 62%
rename from src/cms.c
rename to src/lib/sefht/cms.c
index 7651c0d606aa0d02fd69393c6e622b0520db9d1e..b76720e6e3949b63198fa237fedb419924be0ecb 100644
--- a/src/cms.c
+++ b/src/lib/sefht/cms.c
@@ -25,67 +25,71 @@
 #include <stdlib.h>
 
 #include "macro.h"
-#include "error.h"
 #include "log.h"
+#include "status.h"
 
 #include "data.h"
 
 #include "cms.h"
 
 
+struct SH_Cms
+{
+	/*@only@*/ SH_Data * data;
+};
+
+
+/*@null@*/
+/*@only@*/
 struct SH_Cms *
-SH_Cms_new (struct SH_Error * error)
+SH_Cms_new (/*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
 {
 	struct SH_Cms * cms;
 	cms = malloc (sizeof (struct SH_Cms));
 
 	if (cms == NULL)
 	{
-		ERROR1 ("Memory allocation for SH_Cms failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Cms failed.\n");
 
 		return NULL;
 	}
 
-	cms->data = SH_Data_new (error);
+	cms->data = SH_Data_new (status);
 
 	if (cms->data == NULL)
 	{
 		free (cms);
-
 		return NULL;
 	}
 
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
+	set_success (status);
 	return cms;
 }
 
 void
-SH_Cms_free (struct SH_Cms * cms, struct SH_Error * error)
+SH_Cms_free (/*@only@*/ struct SH_Cms * cms)
+	/*@modifies cms->data@*/
+	/*@modifies cms@*/
+	/*@releases cms@*/
 {
-	SH_Data_free (cms->data, NULL);
+	SH_Data_free (cms->data);
 
 	free (cms);
 
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
 	return;
 }
 
 page_t
 SH_Cms_register_page (struct SH_Cms * cms, const char * name,
-		      struct SH_Error * error)
+                      struct SH_Status * status)
+	/*@modifies cms->data@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
 {
-	return SH_Data_register_page (cms->data, name, error);
+	return SH_Data_register_page (cms->data, name, status);
 }
diff --git a/src/cms.h b/src/lib/sefht/cms.h
similarity index 53%
rename from src/cms.h
rename to src/lib/sefht/cms.h
index 683f0cbc10e56d8d3086bce60edab3fb916176c7..73030b1462756a0feac2dc49982e776040b77c7d 100644
--- a/src/cms.h
+++ b/src/lib/sefht/cms.h
@@ -22,23 +22,40 @@
  */
 
 
-#ifndef _CMS_H
-#define _CMS_H
+#ifndef SEFHT_CMS_H
+#define SEFHT_CMS_H
 
-#include "error.h"
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
+
+#include "status.h"
 
 #include "data.h"
 
 
-struct SH_Cms
-{
-	struct SH_Data * data;
-};
+typedef /*@abstract@*/ struct SH_Cms SH_Cms;
+
+
+/*@null@*/
+/*@only@*/
+SH_Cms *
+SH_Cms_new (/*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
 
-struct SH_Cms * SH_Cms_new (struct SH_Error * error);
-void SH_Cms_free (struct SH_Cms * cms, struct SH_Error * error);
+void
+SH_Cms_free (/*@only@*/ SH_Cms * cms)
+	/*@modifies cms@*/
+	/*@releases cms@*/;
 
-page_t SH_Cms_register_page (struct SH_Cms * cms, const char * name,
-			     struct SH_Error * error);
+page_t
+SH_Cms_register_page (struct SH_Cms * cms, const char * name,
+                      struct SH_Status * status)
+	/*@modifies cms@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
 
-#endif /* _CMS_H */
+#endif /* SEFHT_CMS_H */
diff --git a/src/lib/sefht/data.c b/src/lib/sefht/data.c
new file mode 100644
index 0000000000000000000000000000000000000000..4c3da9327d3c997bdd1f3944ec137c339728c605
--- /dev/null
+++ b/src/lib/sefht/data.c
@@ -0,0 +1,306 @@
+/*
+ * data.c
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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 <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "macro.h"
+#include "log.h"
+#include "status.h"
+
+#include "validator.h"
+
+#include "data.h"
+
+
+struct SH_Page
+{
+	page_t id;
+	char * name;
+};
+
+struct SH_Data
+{
+	SH_Validator * validator;
+	size_t page_n;
+	struct SH_Page * pages;
+	page_t last_page;
+};
+
+
+static inline
+page_t
+next_page (struct SH_Data * data)
+	/*@*/
+{
+	if (data->last_page == PAGE_MAX)
+	{
+		return PAGE_ERR;
+	}
+	return data->last_page + 1;
+}
+
+static inline
+bool
+init_validator (/*@special@*/ struct SH_Data * data,
+                /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@allocates data->validator@*/
+	/*@defines *(data->validator)@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+static inline
+void
+init_pages (/*@special@*/ struct SH_Data * data)
+	/*@allocates data->pages@*/
+	/*@defines data->page_n,
+	 *         data->last_page@*/
+	/*@modifies data->page_n@*/
+	/*@modifies data->pages@*/
+	/*@modifies data->last_page@*/;
+
+/*@null@*/
+/*@only@*/
+struct SH_Data *
+SH_Data_new (/*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+
+{
+	struct SH_Data * data;
+	data = malloc (sizeof (struct SH_Data));
+
+	if (data == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Data failed.\n");
+
+		return NULL;
+	}
+
+	if (!init_validator (data, status))
+	{
+
+/* dangerous call to silence splint, should never be executed. */
+#ifdef S_SPLINT_S
+		free (data->validator);
+#endif
+
+		free (data);
+
+		return NULL;
+	}
+
+	init_pages (data);
+
+	set_success (status);
+	return data;
+}
+
+static inline
+void
+free_validator (/*@special@*/ struct SH_Data * data)
+	/*@modifies data->validator@*/
+	/*@releases data->validator@*/;
+
+static inline
+void
+free_pages (/*@special@*/ struct SH_Data * data)
+	/*@modifies data->pages@*/
+	/*@releases data->pages@*/
+	/*@requires maxRead(data->pages) == (data->page_n - 1)@*/;
+
+void
+SH_Data_free (/*@only@*/ struct SH_Data * data)
+	/*@modifies data->validator@*/
+	/*@modifies data->pages@*/
+	/*@modifies data@*/
+	/*@releases data@*/
+{
+	free_validator (data);
+	free_pages (data);
+
+	free (data);
+
+	return;
+}
+
+static inline
+bool
+init_validator (struct SH_Data * data,
+                /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@allocates data->validator@*/
+	/*@defines *(data->validator)@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	data->validator = SH_Validator_new (status);
+
+	if (data->validator == NULL)
+	{
+		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;
+}
+
+static inline
+void
+free_validator (/*@special@*/ struct SH_Data * data)
+	/*@modifies data->validator@*/
+	/*@releases data->validator@*/
+{
+	SH_Validator_free (data->validator);
+	return;
+}
+
+static inline
+void
+init_pages (/*@special@*/ struct SH_Data * data)
+	/*@allocates data->pages@*/
+	/*@defines data->page_n,
+	 *         data->last_page@*/
+	/*@modifies data->page_n@*/
+	/*@modifies data->pages@*/
+	/*@modifies data->last_page@*/
+{
+	data->page_n = 0;
+	data->pages = malloc (0);
+	data->last_page = PAGE_ERR;
+
+	return;
+}
+
+static inline
+void
+free_pages (/*@special@*/ struct SH_Data * data)
+	/*@modifies data->pages@*/
+	/*@releases data->pages@*/
+	/*@requires maxRead(data->pages) == (data->page_n - 1)@*/
+{
+	size_t index;
+
+	for (index = 0; index < data->page_n; index++)
+	{
+		free (data->pages[index].name);
+	}
+
+	free (data->pages);
+	return;
+}
+
+page_t
+SH_Data_register_page (struct SH_Data * data, const char * name,
+                       struct SH_Status * status)
+	/*@modifies data->page_n@*/
+	/*@modifies data->pages@*/
+	/*@modifies data->last_page@*/
+	/*@requires maxRead(data->pages) == (data->page_n - 1)@*/
+	/*@ensures maxRead(data->pages) == (data->page_n - 1)@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_Page * new_pages;
+	page_t page_id;
+
+	page_id = next_page (data);
+	/* abort on overflow */
+	if ((page_id == PAGE_ERR) || (data->page_n == SIZE_MAX))
+	{
+		set_status (status, E_DOMAIN, 0,
+		            "Maximum number of pages reached.\n");
+
+		return PAGE_ERR;
+	}
+
+	new_pages = realloc (data->pages,
+	                     sizeof (struct SH_Page)
+	                     * (data->page_n + 1));
+
+	if (new_pages == NULL)
+	{
+		set_status (status, E_ALLOC, 5,
+		           "Memory allocation for page data failed.\n");
+
+/* bad code to silence splint, should never be executed. */
+#ifdef S_SPLINT_S
+		data->pages = (void *) 0x12345;
+#endif
+
+		return PAGE_ERR;
+	}
+
+	new_pages[data->page_n].id = page_id;
+	new_pages[data->page_n].name = strdup (name);
+
+	if (new_pages[data->page_n].name == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		           "Memory allocation for page data failed.\n");
+
+		data->pages = new_pages;
+		return PAGE_ERR;
+	}
+
+	/* commit changes */
+	data->pages = new_pages;
+	data->last_page = page_id;
+	data->page_n++;
+
+	set_success (status);
+
+	return data->last_page;
+}
diff --git a/src/data.h b/src/lib/sefht/data.h
similarity index 54%
rename from src/data.h
rename to src/lib/sefht/data.h
index 4b502d308f62163ba27aafb5ac09812882b7d21d..8aa817b3e33e49b731fc3e8022124edbb6703147 100644
--- a/src/data.h
+++ b/src/lib/sefht/data.h
@@ -22,41 +22,47 @@
  */
 
 
-#ifndef _DATA_H
-#define _DATA_H
+#ifndef SEFHT_DATA_H
+#define SEFHT_DATA_H
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
 
 #include <limits.h>
 
-#include "error.h"
+#include "status.h"
 
 #include "validator.h"
 
 
-typedef unsigned int page_t;
+typedef /*@abstract@*/ unsigned int page_t;
 #define PAGE_ERR 0
 #define PAGE_MIN 1
 #define PAGE_MAX UINT_MAX
-#define NEXT_PAGE(page) page++
 
-struct SH_Page
-{
-	page_t id;
-	char * name;
-};
+typedef /*@abstract@*/ struct SH_Data SH_Data;
 
-struct SH_Data
-{
-	struct SH_Validator * validator;
-	unsigned int page_n;
-	struct SH_Page * pages;
-	page_t last_page;
-};
 
-struct SH_Data * SH_Data_new (struct SH_Error * error);
-void SH_Data_free (struct SH_Data * data, struct SH_Error * error);
+/*@null@*/
+/*@only@*/
+SH_Data *
+SH_Data_new (/*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
 
+void
+SH_Data_free (/*@only@*/ struct SH_Data * data)
+	/*@modifies data@*/
+	/*@releases data@*/;
 
-page_t SH_Data_register_page (struct SH_Data * data, const char * name,
-			      struct SH_Error * error);
+page_t
+SH_Data_register_page (SH_Data * data, const char * name,
+                       struct SH_Status * status)
+	/*@modifies data@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
 
-#endif /* _DATA_H */
+#endif /* SEFHT_DATA_H */
diff --git a/src/lib/sefht/fragment.c b/src/lib/sefht/fragment.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a306bd388983053c8d14ed6a0d5fd9f9de15a57
--- /dev/null
+++ b/src/lib/sefht/fragment.c
@@ -0,0 +1,75 @@
+/*
+ * fragment.c
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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 "macro.h"
+#include "log.h"
+#include "status.h"
+
+#include "text.h"
+
+#include "fragment.h"
+
+
+#include "fragment_data.c"
+
+/*@null@*/
+/*@only@*/
+struct SH_Fragment *
+SH_Fragment_copy (const struct SH_Fragment * fragment,
+                  /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	return fragment->methods->copy (fragment, status);
+}
+
+void
+SH_Fragment_free (/*@only@*/ struct SH_Fragment * fragment)
+	/*@modifies fragment@*/
+	/*@releases fragment@*/
+{
+	fragment->methods->free (fragment);
+	return;
+}
+
+/*@null@*/
+/*@only@*/
+SH_Text *
+SH_Fragment_to_html (const struct SH_Fragment * fragment,
+                     enum HTML_MODE mode,
+                     unsigned int indent_base,
+                     unsigned int indent_step,
+                     const char * indent_char,
+                     /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	return fragment->methods->to_html (fragment, mode,
+	                                   indent_base,
+	                                   indent_step,
+	                                   indent_char,
+	                                   status);
+}
diff --git a/src/lib/sefht/fragment.h b/src/lib/sefht/fragment.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2602e810ed359ed3ab32df60a59dab440952aa0
--- /dev/null
+++ b/src/lib/sefht/fragment.h
@@ -0,0 +1,74 @@
+/*
+ * fragment.h
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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_FRAGMENT_H
+#define SEFHT_FRAGMENT_H
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
+
+#include "status.h"
+
+#include "data.h"
+#include "text.h"
+
+
+enum HTML_MODE
+{
+	INLINE,
+	WRAP
+};
+
+typedef /*@abstract@*/ struct SH_Fragment SH_Fragment;
+
+
+/*@null@*/
+/*@only@*/
+struct SH_Fragment *
+SH_Fragment_copy (const SH_Fragment * fragment,
+                  /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+void
+SH_Fragment_free (/*@only@*/ SH_Fragment * fragment)
+	/*@modifies fragment@*/
+	/*@releases fragment@*/;
+
+/*@null@*/
+/*@only@*/
+SH_Text *
+SH_Fragment_to_html (const struct SH_Fragment * fragment,
+                     enum HTML_MODE mode,
+                     unsigned int indent_base,
+                     unsigned int indent_step,
+                     const char * indent_char,
+                     /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+#endif /* SEFHT_FRAGMENT_H */
diff --git a/src/lib/sefht/fragment_class.c b/src/lib/sefht/fragment_class.c
new file mode 100644
index 0000000000000000000000000000000000000000..3a478d1f017f5d57b0554c92f921e7dc0b6eaeeb
--- /dev/null
+++ b/src/lib/sefht/fragment_class.c
@@ -0,0 +1,93 @@
+/*
+ * fragment_class.c
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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_FRAGMENT_CLASS_C
+#define SEFHT_FRAGMENT_CLASS_C
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
+
+#include "status.h"
+
+#include "data.h"
+#include "text.h"
+
+
+#ifdef SEFHT_COMPILATION
+#include "fragment_data.c"
+
+static inline
+void
+init_fragment (/*@out@*/ struct SH_Fragment * fragment,
+               /*@dependent@*/ SH_Data * data,
+               const enum SH_FRAGMENT_TYPE type,
+               /*@shared@*/
+               const struct fragment_methods * const methods)
+	/*@modifies fragment->data@*/
+	/*@modifies fragment->type@*/
+	/*@modifies fragment->methods@*/
+{
+	fragment->data = data;
+	fragment->type = type;
+	fragment->methods = methods;
+
+	return;
+}
+
+static inline
+void
+free_fragment (struct SH_Fragment * fragment)
+	/*@modifies fragment@*/
+	/*@releases fragment@*/
+{
+	(void) fragment;
+	return;
+}
+
+static inline
+void
+copy_fragment (/*@out@*/ struct SH_Fragment * copy,
+               const struct SH_Fragment * fragment)
+	/*@modifies copy->data@*/
+	/*@modifies copy->type@*/
+	/*@modifies copy->methods@*/
+{
+	copy->data = fragment->data;
+	copy->type = fragment->type;
+	copy->methods = fragment->methods;
+
+	return;
+}
+
+static inline
+enum SH_FRAGMENT_TYPE
+get_type (const struct SH_Fragment * fragment)
+	/*@*/
+{
+	return fragment->type;
+}
+
+#endif /* SEFHT_COMPILATION */
+#endif /* SEFHT_FRAGMENT_CLASS_C */
diff --git a/src/lib/sefht/fragment_data.c b/src/lib/sefht/fragment_data.c
new file mode 100644
index 0000000000000000000000000000000000000000..f55a71c764e29281f9b748ea258d97aab6b7aa31
--- /dev/null
+++ b/src/lib/sefht/fragment_data.c
@@ -0,0 +1,86 @@
+/*
+ * fragment_data.c
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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_FRAGMENT_DATA_C
+#define SEFHT_FRAGMENT_DATA_C
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
+
+#include "status.h"
+
+#include "data.h"
+#include "text.h"
+
+
+#ifdef SEFHT_COMPILATION
+
+enum SH_FRAGMENT_TYPE
+{
+	NODE
+};
+
+struct SH_Fragment;
+
+struct fragment_methods
+{
+	/*@null@*/
+	/*@only@*/
+	struct SH_Fragment *
+	(*copy) (const struct SH_Fragment *,
+	         /*@out@*/ /*@null@*/ struct SH_Status *)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+	void
+	(*free) (/*@only@*/ struct SH_Fragment *)
+	/*@modifies fragment@*/
+	/*@releases fragment@*/;
+
+	/*@null@*/
+	/*@only@*/
+	struct SH_Text *
+	(*to_html) (const struct SH_Fragment *,
+	            enum HTML_MODE,
+	            unsigned int,
+	            unsigned int,
+	            const char *,
+	            /*@out@*/ /*@null@*/ struct SH_Status *)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+};
+
+struct SH_Fragment
+{
+	/*@dependent@*/ SH_Data * data;
+
+	enum SH_FRAGMENT_TYPE type;
+	/*@shared@*/ const struct fragment_methods * methods;
+};
+
+#endif /* SEFHT_COMPILATION */
+#endif /* SEFHT_FRAGMENT_DATA_C */
diff --git a/src/log.h b/src/lib/sefht/log.h
similarity index 77%
rename from src/log.h
rename to src/lib/sefht/log.h
index 8e5c3a7c8d95ac096118975f85277d5911044ba1..2f81d238db1c48be1d2fa8c09586b25a9148cb2f 100644
--- a/src/log.h
+++ b/src/lib/sefht/log.h
@@ -22,15 +22,17 @@
  */
 
 
-#ifndef _LOG_H
-#define _LOG_H
+#ifndef SEFHT_LOG_H
+#define SEFHT_LOG_H
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
 
 #include <stdio.h>
 
 
 #define ERROR1(message) printf (message)
-#define ERROR2(message, arg1) printf (message, arg1)
-#define ERROR3(message, arg1, arg2) printf (message, arg1, arg2)
-#define ERROR4(message, arg1, arg2, arg3) printf (message, arg1, arg2, arg3)
+#define ERROR_(message, ...) printf (message, __VA_ARGS__)
 
-#endif /* _LOG_H */
+#endif /* SEFHT_LOG_H */
diff --git a/src/macro.h b/src/lib/sefht/macro.h
similarity index 81%
rename from src/macro.h
rename to src/lib/sefht/macro.h
index 844daa805a74ab15b2c89c9bcfc649f7971155f6..71286e65492069c6dbd47885ed3bd43e53173830 100644
--- a/src/macro.h
+++ b/src/lib/sefht/macro.h
@@ -22,12 +22,16 @@
  */
 
 
-#ifndef _MACRO_H
-#define _MACRO_H
+#ifndef SEFHT_MACRO_H
+#define SEFHT_MACRO_H
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
 
 #include <stdbool.h>
 
 #define TRUE (bool) 1
 #define FALSE (bool) 0
 
-#endif /* _MACRO_H */
+#endif /* SEFHT_MACRO_H */
diff --git a/src/lib/sefht/node_fragment.c b/src/lib/sefht/node_fragment.c
new file mode 100644
index 0000000000000000000000000000000000000000..b1294cdf3288ffa92432734f8b314531a4aaf956
--- /dev/null
+++ b/src/lib/sefht/node_fragment.c
@@ -0,0 +1,594 @@
+/*
+ * node_fragment.c
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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 <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "macro.h"
+#include "log.h"
+#include "status.h"
+
+#include "data.h"
+#include "text.h"
+#include "validator.h"
+
+#include "fragment.h"
+
+#include "node_fragment.h"
+
+
+#include "fragment_class.c"
+
+struct SH_NodeFragment
+{
+	struct SH_Fragment base;
+
+	char * tag;
+
+	size_t child_n;
+	struct SH_Fragment ** childs;
+};
+
+#define OPEN_TAG_BEGIN "<"
+#define OPEN_TAG_END ">"
+#define CLOSE_TAG_BEGIN "</"
+#define CLOSE_TAG_END ">"
+
+#define NEWLINE "\n"
+
+
+static const struct fragment_methods methods = {
+	(struct SH_Fragment * (*)(const struct SH_Fragment *,
+	                          /*@out@*/ /*@null@*/
+	                          struct SH_Status *))
+	SH_NodeFragment_deepcopy,
+
+	(void (*)(/*@only@*/ struct SH_Fragment *))
+	SH_NodeFragment_free,
+
+	(struct SH_Text * (*)(const struct SH_Fragment *,
+	                      enum HTML_MODE,
+	                      unsigned int, unsigned int, const char *,
+	                      /*@out@*/ /*@null@*/ struct SH_Status *))
+	SH_NodeFragment_to_html
+};
+
+
+/*@null@*/
+/*@only@*/
+struct SH_Fragment /*@alt struct SH_NodeFragment@*/ *
+SH_NodeFragment_new (const char * tag,
+                     /*@dependent@*/ SH_Data * data,
+                     /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals NODE,
+	           fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_NodeFragment * fragment;
+
+	fragment = malloc (sizeof (struct SH_NodeFragment));
+	if (fragment == NULL)
+	{
+		set_status (status, E_ALLOC, 3,
+		            "Memory allocation for "
+		            "SH_NodeFragment failed.\n");
+
+		return NULL;
+	}
+
+	init_fragment (&(fragment->base), data, NODE, &methods);
+
+	fragment->tag = strdup (tag);
+	if (fragment->tag == NULL)
+	{
+		set_status (status, E_ALLOC, 3,
+		            "Memory allocation for "
+		            "fragment tag failed.\n");
+
+		free (fragment);
+
+		return NULL;
+	}
+
+
+	fragment->child_n = 0;
+	fragment->childs = malloc (0);
+
+	set_success (status);
+
+	return (struct SH_Fragment *) fragment;
+}
+
+void
+SH_NodeFragment_free (/*@only@*/ struct SH_NodeFragment * fragment)
+	/*@modifies fragment@*/
+	/*@releases fragment@*/
+{
+	size_t index;
+
+	free (fragment->tag);
+
+	for (index = 0; index < fragment->child_n; index++)
+	{
+		SH_Fragment_free (fragment->childs[index]);
+	}
+	free (fragment->childs);
+
+	free_fragment (&fragment->base);
+
+	free (fragment);
+
+	return;
+}
+
+/*@null@*/
+/*@only@*/
+struct SH_Fragment /*@alt struct SH_NodeFragment@*/ *
+SH_NodeFragment_copy (const struct SH_NodeFragment * fragment,
+                      /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_NodeFragment * copy;
+
+	copy = malloc (sizeof (struct SH_NodeFragment));
+	if (copy == NULL)
+	{
+		set_status (status, E_ALLOC, 3,
+		            "Memory allocation for "
+		            "SH_NodeFragment failed.\n");
+
+		return NULL;
+	}
+
+	copy_fragment (&(copy->base), &(fragment->base));
+
+	copy->tag = strdup (fragment->tag);
+	if (copy->tag == NULL)
+	{
+		set_status (status, E_ALLOC, 3,
+		            "Memory allocation for "
+		            "fragment tag failed.\n");
+
+		free (copy);
+
+		return NULL;
+	}
+
+
+	copy->child_n = 0;
+	copy->childs = malloc (0);
+
+	set_success (status);
+
+	return (struct SH_Fragment *) copy;
+}
+
+/*@null@*/
+/*@only@*/
+struct SH_Fragment /*@alt struct SH_NodeFragment@*/ *
+SH_NodeFragment_deepcopy (const struct SH_NodeFragment * fragment,
+                          /*@out@*/ /*@null@*/
+                          struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_NodeFragment * copy;
+	struct SH_Fragment * child;
+	size_t index;
+
+	copy = malloc (sizeof (struct SH_NodeFragment));
+	if (copy == NULL)
+	{
+		set_status (status, E_ALLOC, 3,
+		            "Memory allocation for "
+		            "SH_NodeFragment failed.\n");
+
+		return NULL;
+	}
+
+	copy_fragment (&(copy->base), &(fragment->base));
+
+	copy->tag = strdup (fragment->tag);
+	if (copy->tag == NULL)
+	{
+		set_status (status, E_ALLOC, 3,
+		            "Memory allocation for "
+		            "fragment tag failed.\n");
+
+		free (copy);
+
+		return NULL;
+	}
+
+
+	copy->child_n = fragment->child_n;
+	copy->childs = malloc (sizeof (struct SH_NodeFragment *)
+	                       * fragment->child_n);
+
+	if (copy->child_n != 0 && copy->childs == NULL)
+	{
+		set_status (status, E_ALLOC, 5,
+		            "Memory allocation for "
+		            "fragment child failed.\n");
+
+		free (copy->tag);
+/* dangerous call to silence splint, should never be executed. */
+#ifdef S_SPLINT_S
+		free (copy->childs);
+#endif
+		free (copy);
+
+		return NULL;
+	}
+
+	for (index = 0; index < fragment->child_n; index++)
+	{
+		child = SH_Fragment_copy (fragment->childs[index],
+		                          status);
+
+		if (child == NULL)
+		{
+			copy->child_n = index;
+			SH_NodeFragment_free (copy);
+
+			return NULL;
+		}
+
+		copy->childs[index] = child;
+	}
+
+	set_success (status);
+
+	return (struct SH_Fragment *) copy;
+}
+
+bool
+SH_Fragment_is_NodeFragment (const struct SH_Fragment * fragment)
+	/*@*/
+{
+	return get_type (fragment) == NODE;
+}
+
+/*@null@*/
+/*@only@*/
+char *
+SH_NodeFragment_get_tag (const struct SH_NodeFragment * fragment,
+                         /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	char * tag;
+
+	tag = strdup (fragment->tag);
+
+	if (tag == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for "
+		            "fragment tag failed.\n");
+
+		return NULL;
+	}
+
+	set_success (status);
+
+	return tag;
+}
+
+/*@null@*/
+/*@observer@*/
+struct SH_Fragment *
+SH_NodeFragment_get_child (const struct SH_NodeFragment * fragment,
+                           size_t index,
+                           /*@out@*/ /*@null@*/
+                           struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	if (index >= fragment->child_n)
+	{
+		set_status (status, E_VALUE, 2,
+		            "Fragment: Child index out of range.\n");
+
+		return NULL;
+	}
+
+	set_success (status);
+
+	return fragment->childs[index];
+}
+
+bool
+SH_NodeFragment_is_child (const struct SH_NodeFragment * fragment,
+                          const struct SH_Fragment * child)
+	/*@*/
+{
+	size_t index;
+
+	for (index = 0; index < fragment->child_n; index++)
+	{
+		if (fragment->childs[index] == child)
+		{
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+bool
+SH_NodeFragment_is_descendant (const struct SH_NodeFragment * fragment,
+                          const struct SH_Fragment * child)
+	/*@*/
+{
+	size_t index;
+
+	for (index = 0; index < fragment->child_n; index++)
+	{
+		if (fragment->childs[index] == child
+		|| (SH_Fragment_is_NodeFragment (child)
+		    && SH_NodeFragment_is_descendant (
+		                        (struct SH_NodeFragment *)
+		                        fragment->childs[index],
+		                        child)))
+		{
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+bool
+SH_NodeFragment_append_child (struct SH_NodeFragment * fragment,
+                              /*@only@*/ struct SH_Fragment * child,
+                              /*@out@*/ /*@null@*/
+                              struct SH_Status * status)
+	/*@modifies fragment->childs@*/
+	/*@modifies fragment->child_n@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_Fragment ** new_childs;
+
+	new_childs = realloc (fragment->childs,
+	                      sizeof (struct SH_Fragment *)
+	                      * (fragment->child_n + 1));
+
+	if (new_childs == NULL)
+	{
+		set_status (status, E_ALLOC, 6,
+		            "Memory allocation for "
+		            "fragment child failed.\n");
+
+/* bad code to silence splint, should never be executed. */
+#ifdef S_SPLINT_S
+				fragment->childs = (void *) 0x12345;
+#endif
+		return FALSE;
+	}
+
+	new_childs[fragment->child_n] = child;
+
+	fragment->childs = new_childs;
+	fragment->child_n++;
+
+	set_success (status);
+
+	return TRUE;
+}
+
+/*@null@*/
+/*@only@*/
+struct SH_Text *
+SH_NodeFragment_to_html (const struct SH_NodeFragment * fragment,
+                         enum HTML_MODE mode,
+                         unsigned int indent_base,
+                         unsigned int indent_step,
+                         const char * indent_char,
+                         /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_Text * html;
+	struct SH_Text * child;
+	struct SH_Text * indent_text;
+	size_t index;
+
+	html = SH_Text_new (status);
+	if (html == NULL)
+	{
+		return NULL;
+	}
+
+	if (mode == WRAP)
+	{
+		indent_text = SH_Text_new (status);
+		if (indent_text == NULL)
+		{
+			SH_Text_free (html);
+			return NULL;
+		}
+
+		for (index = 0; ((unsigned int) index) < indent_base;
+		     index++)
+		{
+			if (!SH_Text_append_string (indent_text,
+			                            indent_char,
+			                            status))
+			{
+				SH_Text_free (html);
+				SH_Text_free (indent_text);
+				return NULL;
+			}
+		}
+
+		if (!SH_Text_append_text (html, indent_text, status))
+		{
+			SH_Text_free (html);
+			SH_Text_free (indent_text);
+			return NULL;
+		}
+	}
+
+	if (!SH_Text_append_string (html, OPEN_TAG_BEGIN, status))
+	{
+		SH_Text_free (html);
+
+		if (mode == WRAP)
+		{
+			SH_Text_free (indent_text);
+		}
+
+		return NULL;
+	}
+
+	if (!SH_Text_append_string (html, fragment->tag, status))
+	{
+		SH_Text_free (html);
+
+		if (mode == WRAP)
+		{
+			SH_Text_free (indent_text);
+		}
+
+		return NULL;
+	}
+
+	if (!SH_Text_append_string (html, OPEN_TAG_END, status))
+	{
+		SH_Text_free (html);
+
+		if (mode == WRAP)
+		{
+			SH_Text_free (indent_text);
+		}
+
+		return NULL;
+	}
+
+	if (mode == WRAP)
+	{
+		if (!SH_Text_append_string (html, NEWLINE, status))
+		{
+			SH_Text_free (html);
+			SH_Text_free (indent_text);
+			return NULL;
+		}
+	}
+
+	for (index = 0; index < fragment->child_n; index++)
+	{
+		child = SH_Fragment_to_html (fragment->childs[index],
+					     mode,
+					     indent_base + indent_step,
+					     indent_step,
+					     indent_char,
+					     status);
+
+		if (child == NULL)
+		{
+			SH_Text_free (html);
+
+			if (mode == WRAP)
+			{
+				SH_Text_free (indent_text);
+			}
+
+			return NULL;
+		}
+
+		SH_Text_join (html, child);
+	}
+
+	if (mode == WRAP)
+	{
+		if (!SH_Text_append_text (html, indent_text, status))
+		{
+			SH_Text_free (html);
+			SH_Text_free (indent_text);
+			return NULL;
+		}
+	}
+
+	if (!SH_Text_append_string (html, CLOSE_TAG_BEGIN, status))
+	{
+		SH_Text_free (html);
+
+		if (mode == WRAP)
+		{
+			SH_Text_free (indent_text);
+		}
+
+		return NULL;
+	}
+
+	if (!SH_Text_append_string (html, fragment->tag, status))
+	{
+		SH_Text_free (html);
+
+		if (mode == WRAP)
+		{
+			SH_Text_free (indent_text);
+		}
+
+		return NULL;
+	}
+
+	if (!SH_Text_append_string (html, CLOSE_TAG_END, status))
+	{
+		SH_Text_free (html);
+
+		if (mode == WRAP)
+		{
+			SH_Text_free (indent_text);
+		}
+
+		return NULL;
+	}
+
+	if (mode == WRAP)
+	{
+		if (!SH_Text_append_string (html, NEWLINE, status))
+		{
+			SH_Text_free (html);
+			SH_Text_free (indent_text);
+			return NULL;
+		}
+
+		SH_Text_free (indent_text);
+	}
+
+	set_success (status);
+
+	return html;
+}
diff --git a/src/lib/sefht/node_fragment.h b/src/lib/sefht/node_fragment.h
new file mode 100644
index 0000000000000000000000000000000000000000..46be532032aa5df8eb7fd6b8d4db5b0648d5a442
--- /dev/null
+++ b/src/lib/sefht/node_fragment.h
@@ -0,0 +1,136 @@
+/*
+ * node_fragment.h
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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_NODE_FRAGMENT_H
+#define SEFHT_NODE_FRAGMENT_H
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#status "Only <sefht/sefht.h> can be included directly."
+#endif
+
+#include "status.h"
+
+#include "data.h"
+#include "text.h"
+
+#include "fragment.h"
+
+
+#define INDENT_TEXT "\t"
+
+typedef /*@abstract@*/ struct SH_NodeFragment SH_NodeFragment;
+
+
+/*@null@*/
+/*@only@*/
+SH_Fragment /*@alt SH_NodeFragment@*/ *
+SH_NodeFragment_new (const char * tag,
+                     /*@dependent@*/ SH_Data * data,
+                     /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+void
+SH_NodeFragment_free (/*@only@*/ SH_NodeFragment * fragment)
+	/*@modifies fragment@*/
+	/*@releases fragment@*/;
+
+/*@null@*/
+/*@only@*/
+SH_Fragment /*@alt SH_NodeFragment@*/ *
+SH_NodeFragment_copy (const SH_NodeFragment * fragment,
+                      /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+/*@null@*/
+/*@only@*/
+SH_Fragment /*@alt SH_NodeFragment@*/ *
+SH_NodeFragment_deepcopy (const SH_NodeFragment * fragment,
+                          /*@out@*/ /*@null@*/
+                          struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+bool
+SH_Fragment_is_NodeFragment (const SH_Fragment * fragment)
+	/*@*/;
+
+/*@null@*/
+/*@only@*/
+char *
+SH_NodeFragment_get_tag (const SH_NodeFragment * fragment,
+                         /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+/*@null@*/
+/*@observer@*/
+SH_Fragment *
+SH_NodeFragment_get_child (const SH_NodeFragment * fragment,
+                           size_t index,
+                           /*@out@*/ /*@null@*/
+                           struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+bool
+SH_NodeFragment_is_child (const SH_NodeFragment * fragment,
+                          const SH_Fragment * child)
+	/*@*/;
+
+bool
+SH_NodeFragment_is_descendant (const struct SH_NodeFragment * fragment,
+                          const struct SH_Fragment * child)
+	/*@*/;
+
+bool
+SH_NodeFragment_append_child (struct SH_NodeFragment * fragment,
+                              /*@only@*/ struct SH_Fragment * child,
+                              /*@out@*/ /*@null@*/
+                              struct SH_Status * status)
+	/*@modifies fragment@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+/*@null@*/
+/*@only@*/
+SH_Text *
+SH_NodeFragment_to_html (const SH_NodeFragment * fragment,
+                         enum HTML_MODE mode,
+                         unsigned int indent_base,
+                         unsigned int indent_step,
+                         const char * indent_char,
+                         /*@out@*/ /*@null@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+#endif /* SEFHT_NODE_FRAGMENT_H */
diff --git a/src/sefht.h b/src/lib/sefht/sefht.h
similarity index 72%
rename from src/sefht.h
rename to src/lib/sefht/sefht.h
index 74f46dbd5ba8f931e6e3a18d1a58c777900f805b..b94c2672cbd221d297d60003e865547022eacb4a 100644
--- a/src/sefht.h
+++ b/src/lib/sefht/sefht.h
@@ -22,7 +22,22 @@
  */
 
 
-#ifndef _SEFHT_H
-#define _SEFHT_H
+#ifndef SEFHT_SEFHT_H
+#define SEFHT_SEFHT_H
 
-#endif /* _SEFHT_H */
+#define SEFHT_SEFHT_H_INSIDE
+
+#include "log.h"
+#include "macro.h"
+#include "status.h"
+
+#include "cms.h"
+#include "data.h"
+#include "fragment.h"
+#include "node_fragment.h"
+#include "text.h"
+#include "validator.h"
+
+#undef SEFHT_SEFHT_H_INSIDE
+
+#endif /* SEFHT_SEFHT_H */
diff --git a/src/lib/sefht/status.h b/src/lib/sefht/status.h
new file mode 100644
index 0000000000000000000000000000000000000000..4b4b479ee2b7fb623f650127ab686e3b939f49ae
--- /dev/null
+++ b/src/lib/sefht/status.h
@@ -0,0 +1,127 @@
+/*
+ * status.h
+ *
+ * Copyright 2022 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_STATUS_H
+#define SEFHT_STATUS_H
+
+#include <errno.h>
+#include <string.h>
+
+#include "macro.h"
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
+
+
+#ifdef SEFHT_COMPILATION
+
+#define succeed(STATUS)                                                \
+	(((STATUS) != NULL) && ((STATUS)->status == SUCCESS))
+
+#define failed(STATUS)                                                 \
+	(((STATUS) != NULL) && ((STATUS)->status != SUCCESS))
+
+#define unknown(STATUS) ((STATUS) == NULL)
+
+#define set_status(STATUS, ERROR, OFFSET, MESSAGE)                     \
+do                                                                     \
+{                                                                      \
+	if ((STATUS) != NULL)                                          \
+	{                                                              \
+		(STATUS)->status = (ERROR);                            \
+		(STATUS)->errno_ = ((ERROR) == SUCCESS) ? 0: errno;    \
+		(STATUS)->file = (__FILE__);                           \
+		(STATUS)->function = (__FUNCTION__);                   \
+		(STATUS)->line = (unsigned long) ((__LINE__)-(OFFSET));\
+		(STATUS)->message = (MESSAGE);                         \
+	};                                                             \
+                                                                       \
+	if ((MESSAGE) != NULL)                                         \
+	{                                                              \
+		ERROR1 ((MESSAGE));                                    \
+	}                                                              \
+}                                                                      \
+while (FALSE)
+
+#define set_status_(STATUS, ERROR, OFFSET, LENGTH, MESSAGE, ...)       \
+do                                                                     \
+{                                                                      \
+	if ((STATUS) != NULL)                                          \
+	{                                                              \
+		(STATUS)->status = (ERROR);                            \
+		(STATUS)->errno_ = ((ERROR) == SUCCESS) ? 0: errno;    \
+		(STATUS)->file = (__FILE__);                           \
+		(STATUS)->function = (__FUNCTION__);                   \
+		(STATUS)->line = (unsigned long) ((__LINE__)-(OFFSET));\
+		(STATUS)->message = malloc(strlen(MESSAGE)+1 + LENGTH);\
+		if ((STATUS)->message == NULL)                         \
+		{                                                      \
+			set_status ((STATUS), E_ALLOC, (OFFSET),       \
+			            "malloc failed while generating "  \
+			            "error message");                  \
+		}                                                      \
+		sprintf ((STATUS)->message, (MESSAGE), __VA_ARGS__);   \
+	};                                                             \
+                                                                       \
+	ERROR_ ((MESSAGE), __VA_ARGS__);                               \
+}                                                                      \
+while (FALSE)
+
+#define set_success(STATUS) set_status(STATUS, SUCCESS, 0, NULL)
+
+#define _status_preinit(STATUS)                                        \
+do {                                                                   \
+	(STATUS).status = UNDEFINED;                                   \
+	(STATUS).errno_ = 0;                                           \
+	(STATUS).file = NULL;                                          \
+	(STATUS).line = 0;                                             \
+	(STATUS).message = NULL;                                       \
+}                                                                      \
+while (FALSE)
+
+#endif /* SEFHT_COMPILATION */
+
+
+struct SH_Status
+{
+	enum
+	{
+		UNDEFINED,
+		SUCCESS,
+		E_ALLOC,
+		E_DOMAIN,
+		E_VALUE
+	} status;
+
+	int errno_;
+
+	/*@observer@*/ const char * file;
+	/*@observer@*/ const char * function;
+	unsigned long int line;
+
+	/*@observer@*/ /*@null@*/ char * message;
+};
+
+#endif /* SEFHT_STATUS_H */
diff --git a/src/lib/sefht/text.c b/src/lib/sefht/text.c
new file mode 100644
index 0000000000000000000000000000000000000000..b071c508670cccfd7bf68bdbe316cbc313b05776
--- /dev/null
+++ b/src/lib/sefht/text.c
@@ -0,0 +1,1054 @@
+/*
+ * text.c
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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 <errno.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "macro.h"
+#include "log.h"
+#include "status.h"
+
+#include "text.h"
+
+
+#ifndef CHUNK_SIZE
+#define CHUNK_SIZE 64
+#endif /* CHUNK_SIZE */
+
+#if CHUNK_SIZE == 0
+#error "CHUNK_SIZE can't be 0."
+#endif /* CHUNK_SIZE == 0 */
+
+struct text_segment;
+struct text_segment
+{
+	size_t length;
+	size_t size;
+	char * text;
+	/*@relnull@*/ struct text_segment * next;
+};
+
+struct SH_Text
+{
+	/*@notnull@*/ struct text_segment * data;
+};
+
+
+/*@null@*/
+/*@only@*/
+struct SH_Text *
+SH_Text_new (/*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_Text * text;
+	text = malloc (sizeof (struct SH_Text));
+
+	if (text == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Text failed.\n");
+
+		return NULL;
+	}
+
+	text->data = malloc (sizeof (struct text_segment));
+
+	if (text->data == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Text failed.\n");
+
+		free (text);
+
+		return NULL;
+	}
+
+	text->data->length = 0;
+	text->data->text = malloc (CHUNK_SIZE * sizeof (char));
+
+	if (text->data->text == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Text failed.\n");
+
+		free (text->data);
+		free (text);
+
+		return NULL;
+	}
+
+	text->data->text[0] = (char) 0;
+	text->data->size = (size_t) CHUNK_SIZE;
+	text->data->next = NULL;
+
+	set_success (status);
+
+	return text;
+}
+
+/*@null@*/
+/*@only@*/
+struct SH_Text *
+SH_Text_new_from_string (const char * string,
+                         /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_Text * text;
+	text = malloc (sizeof (struct SH_Text));
+
+	if (text == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Text failed.\n");
+
+		return NULL;
+	}
+
+	text->data = malloc (sizeof (struct text_segment));
+
+	if (text->data == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Text failed.\n");
+
+		free (text);
+
+		return NULL;
+	}
+
+	text->data->length = strlen (string);
+
+	if (text->data->length == SIZE_MAX)
+	{
+		set_status (status, E_DOMAIN, 2,
+		            "Maximum length of SH_Text reached.\n");
+
+		free (text->data);
+		free (text);
+
+		return NULL;
+	}
+
+	text->data->text = strdup (string);
+
+	if (text->data->text == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Text failed.\n");
+
+		free (text->data);
+		free (text);
+
+		return NULL;
+	}
+
+	text->data->size = text->data->length + 1;
+	text->data->next = NULL;
+
+	set_success (status);
+
+	return text;
+}
+
+void
+SH_Text_free (/*@only@*/ struct SH_Text * text)
+	/*@modifies text@*/
+	/*@releases text@*/
+{
+	struct text_segment * next;
+
+	for (next = text->data; next != NULL; text->data = next)
+	{
+		next = next->next;
+
+		free (text->data->text);
+		free (text->data);
+	}
+
+	free (text);
+
+	return;
+}
+
+/*@null@*/
+/*@only@*/
+struct SH_Text *
+SH_Text_copy (const struct SH_Text * text,
+              /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	const struct text_segment * start;
+	const struct text_segment * end;
+	struct text_segment * copy_seg;
+
+	struct SH_Text * copy;
+	copy = malloc (sizeof (struct SH_Text));
+
+	if (copy == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Text failed.\n");
+
+		return NULL;
+	}
+
+	copy->data = malloc (sizeof (struct text_segment));
+
+	if (copy->data == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Text failed.\n");
+
+		free (copy);
+
+		return NULL;
+	}
+
+	copy->data->next = NULL;
+
+	/* copy_seg is the segment we're currently copying to */
+	copy_seg = copy->data;
+
+	/* start to end are the segments we're copying from. */
+	/* start points to the first segment to copy.
+	 * end points to the first segment not being copied in
+	 * the current iteration. */
+	end = text->data;
+	start = end;
+
+	/* At the beginning of each iteration start == end.
+	 * This is ensured before the loop and at the end of the outer
+	 * loop with the last for loop, which does the actual copying.
+	 *
+	 * Then end is adjusted to point after the last segment to be
+	 * copied. If it isn't NULL afterwards another loop iteration
+	 * has to take place.
+	 *
+	 * With start and end properly set, and the length and size of
+	 * the resulting segment determined, the copying can start,
+	 * which is just a repeated string concatenation.
+	 *
+	 * If another iteration is following, the next segment has to be
+	 * allocated. This can't be done at the beginning, because
+	 * before the first iteration the pointer to the allocated
+	 * segment must be written to copy->data directly to create the
+	 * anchor of the linked list. (So there is no ?->next
+	 * to write to.)
+	 * Actually copy_seg can also be set to &(copy->data) before
+	 * the loop and to &((*copy_seg)->next) after each iteration,
+	 * but that would be complicate things...
+	 * Implementing the loop as for loop and writing the allocation
+	 * code into their head is also not possible, because compound
+	 * statements aren't allowed there.
+	 * Thus the allocating has to take place at the end of the loop
+	 * (if necessary) and the strange if(end==NULL){break;} has to
+	 * stay there to. */
+	/* Actually while (TRUE) would also work, because the loop is
+	 * always aborted with break.
+	 * (See comment above, last paragraph.) */
+	while (end != NULL)
+	{
+		/* Adjusting end and determine length of the resulting
+		 * segment. Actually the end is determined by the
+		 * length, because the segment can't be greather than
+		 * numbers in size_t. Otherwise we can't address
+		 * everything nor save the length. */
+		for (copy_seg->length = 0; end != NULL; end = end->next)
+		{
+			/* Stop moving the end further then what we
+			 * can address. Actually copy_seg->length could
+			 * be one greater, so the comparison could be
+			 * with >=, but copy_seg->size should also be
+			 * expressible and this is always at least by
+			 * one greater, because of the terminating
+			 * NULL-byte. */
+			if (SIZE_MAX - end->length > copy_seg->length)
+			{
+				copy_seg->length += end->length;
+			}
+			/* This can't be moved after the loop, because,
+			 * if the loop exits normally, this must not be
+			 * executed. */
+			else
+			{
+				end = end->next;
+
+				/*@innerbreak@*/
+				break;
+			}
+		}
+
+		/* This addition can not overflow, because in the
+		 * for-loop it was tested with > instead of >= which
+		 * would be appropriate. See comment there. */
+		copy_seg->size = copy_seg->length + 1;
+
+		copy_seg->text = malloc (copy_seg->size * sizeof (char));
+
+		if (copy_seg->text == NULL)
+		{
+			set_status (status, E_ALLOC, 4,
+			            "Memory allocation for SH_Text "
+			            "failed.\n");
+
+			for (copy_seg = copy->data->next;
+			     copy_seg != NULL;
+			     copy->data = copy_seg,
+			     copy_seg = copy_seg->next)
+			{
+				if (copy->data->text != NULL)
+				{
+					free (copy->data->text);
+				}
+
+				free (copy->data);
+			}
+			free (copy->data);
+
+			free (copy);
+
+			return NULL;
+		}
+
+		copy_seg->text[0] = (char) 0;
+
+		/* actual copying */
+		/* Initialization was done since the beginning of
+		 * the outer loop. So think of all this code standing
+		 *   |  here.
+		 *   v        */
+		for (; start != end; start = start->next)
+		{
+			strcat (copy_seg->text, start->text);
+		}
+
+		/* See comment of outer loop. (3rd paragraph) */
+		if (end == NULL)
+		{
+			break;
+		}
+
+		copy_seg->next = malloc (sizeof (struct text_segment));
+
+		if (copy_seg->next == NULL)
+		{
+			set_status (status, E_ALLOC, 4,
+			            "Memory allocation for SH_Text "
+			            "failed.\n");
+
+			for (copy_seg = copy->data->next;
+			     copy_seg != NULL;
+			     copy->data = copy_seg,
+			     copy_seg = copy_seg->next)
+			{
+				if (copy->data->text != NULL)
+				{
+					free (copy->data->text);
+				}
+
+				free (copy->data);
+			}
+			free (copy->data);
+
+			free (copy);
+
+			return NULL;
+		}
+
+		copy_seg = copy_seg->next;
+		copy_seg->next = NULL;
+	}
+
+	/* This could be also moved inside the loop instead of the
+	 * break, because the loop quits always by this break.
+	 * Also see the comment of the outer loop above. */
+	set_success (status);
+
+	return copy;
+}
+
+size_t
+SH_Text_get_length (const struct SH_Text * text,
+                    /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	size_t length;
+	const struct text_segment * seg;
+
+	length = 0;
+	for (seg = text->data; seg != NULL; seg = seg->next)
+	{
+		if (SIZE_MAX - seg->length >= length)
+		{
+			length += seg->length;
+		}
+		else
+		{
+			set_status (status, E_DOMAIN, 6,
+			            "SH_Text: length >= SIZE_MAX\n");
+
+			return SIZE_MAX;
+		}
+	}
+
+	set_success (status);
+
+	return length;
+}
+
+/*@null@*/
+/*@only@*/
+char *
+SH_Text_get_char (const struct SH_Text * text,
+                  size_t index,
+                  /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	char * character;
+	const struct text_segment * seg;
+
+	/* find seg, where the index points to OR set seg to NULL */
+	for (seg = text->data; (seg != NULL) && (index >= seg->length);
+	     index -= seg->length, seg = seg->next);
+
+	if (seg == NULL)
+	{
+		set_status (status, E_VALUE, 2,
+		            "SH_Text: index out of range.\n");
+
+		return NULL;
+	}
+
+	/* allocate char to be returned */
+	character = malloc (sizeof (char));
+
+	if (character == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for SH_Text failed.\n");
+
+		return NULL;
+	}
+
+	/* set char */
+	*character = seg->text[index];
+
+	set_success (status);
+
+	return character;
+}
+
+/*@null@*/
+/*@only@*/
+char *
+SH_Text_get_string (const struct SH_Text * text,
+                    size_t index, size_t offset,
+                    /*@null@*/ /*@out@*/ size_t * length,
+                    /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@modifies length@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	size_t len;
+	char * string;
+	const struct text_segment * start;
+	const struct text_segment * end;
+
+	/* Determine the begin of the requested string.
+	 * start: The segment where the requested string starts.
+	 * index: The index of the first char relative to
+	          the first segment. */
+	for (start = text->data;
+	     (start != NULL) && (index >= start->length);
+	     start = start->next)
+	{
+		/* The subtraction is save, because this is,
+		 * what is tested for in the condition. */
+		index -= start->length;
+	}
+
+	/* The end of text was encountered while looking for
+	 * the start segment. Those the index is out of range. */
+	if (start == NULL)
+	{
+		set_status (status, E_VALUE, 2,
+		            "SH_Text: index out of range.\n");
+
+		if (length != NULL)
+		{
+			*length = 0;
+		}
+
+		return NULL;
+	}
+
+	/* The requested string is fully inside one segment.
+	 * So set end and len offset appropriately.
+	 * Note that offset isn't relative to the segment,
+	 * but to index, but this is what we need, since we
+	 * want to copy relative to index. */
+	if ((offset < SIZE_MAX - index)
+	&& (index + offset <= start->length))
+	{
+		end = start;
+		len = offset;
+	}
+
+	/* The requested string is in more than one segment.
+	 * Find the segment of the text, where the last part of
+	 * the string is contained and set offset relative to it.
+	 * end: The segment where the last part of the string
+	 *      is located.
+	 * offset: The remaining length of the string inside
+	 *         this last segment.
+	 *
+	 * Also the absolute length is needed for allocating the string
+	 * and returning it. This would be the original value of offset,
+	 * but it can not be set directly, because offset could be
+	 * longer then the text actually is. Those both has to be set
+	 * in each iteration over the text, till the last segment
+	 * is encountered (the offset is smaller then the length of
+	 * the segment). */
+	else
+	{
+		/* skip the remaining of the start segment */
+		/* The subtraction is save, because index is relative
+		 * to the start segment (smaller then start->length). */
+		len = start->length - index;
+		/* The subtraction is save, because this is the
+		 * else branch. (offset + index > start->length) */
+		offset -= len;
+
+		/* Iterate over the text till the offset is inside
+		 * the current segment. Actually the comparison has
+		 * to be made against end->next, because, if offset
+		 * points outside of the text, a pointer to the last
+		 * segment has to be preserved. Otherwise we would
+		 * have to iterate over the text another time. */
+		for (end = start;
+		     (end->next != NULL) && (offset > end->next->length);
+		     end = end->next)
+		{
+			/* The subtraction is save because this is,
+			 * what is tested for.
+			 * The addition is save, because len will be
+			 * equal or smaller then the original offset
+			 * and this is also of size_t. Note that
+			 * len = offset;
+			 * is not possible in the first place,
+			 * see the comment before the else branch. */
+			len += end->next->length;
+			offset -= end->next->length;
+		}
+
+		/* We have encountered the end of text. Set offset to
+		 * the length of the last segment (making it smaller);
+		 * end already points to the last (non-NULL) segment. */
+		if (end->next == NULL)
+		{
+			offset = end->length;
+		}
+		/* The end of the requested string is inside the
+		 * next segment. The variable offset is already
+		 * relative to it, so just add it to the length.
+		 * Also set end to the next segment, this can't
+		 * be done earlier, because it may be NULL.
+		 * (See comment before if.) */
+		else
+		{
+			/* The addition is save, because len will be
+			 * equal or smaller then the original offset
+			 * and this is also of size_t.
+			 * Note that len = offset is not possible in
+			 * the first place, see the comment before
+			 * the outer else branch. */
+			len += offset;
+			end = end->next;
+		}
+	}
+
+	/* Allocate the string to be returned. The multiplication is
+	 * save, as long is sizeof (char) == 1. (Should be ...) */
+	string = malloc ((len + 1) * sizeof (char));
+
+	/* Allocating has failed. Nothing has to be freed, because we
+	 * just have some stack vars and pointers to segments. */
+	if (string == NULL)
+	{
+		set_status (status, E_ALLOC, 6,
+		            "Memory allocation for SH_Text failed.\n");
+
+		if (length != NULL)
+		{
+			*length = 0;
+		}
+
+		return NULL;
+	}
+
+	/* add terminating NULL byte. */
+	string[0] = (char) 0;
+
+	/* The requested string is fully inside one segment. */
+	if (start == end)
+	{
+		strncat (string, start->text + index, offset);
+	}
+	/* The requested string is in more than one segment. */
+	else
+	{
+		/* copy part of the start segment */
+		strcat (string, start->text + index);
+
+		/* copy all segments, that are contained in the
+		 * requested string fully */
+		for (start = start->next; start != end;
+		     start = start->next)
+		{
+			strcat (string, start->text);
+		}
+
+		/* copy part of the last segment */
+		strncat (string, end->text, offset);
+	}
+
+
+	if (length != NULL)
+	{
+		*length = len;
+	}
+
+	set_success (status);
+
+	return string;
+}
+
+/*@null@*/
+/*@only@*/
+char *
+SH_Text_get_range (const struct SH_Text * text,
+                   size_t start, size_t end,
+                   /*@null@*/ /*@out@*/ size_t * length,
+                   /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@modifies length@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	size_t len;
+	char * string;
+	const struct text_segment * start_seg;
+	const struct text_segment * end_seg;
+
+	/* Determine the begin of the requested string.
+	 * start_seg: The segment where the requested string starts.
+	 * start: The index of the first char relative to
+	 *        the first segment.
+	 * end: The index after the last char relative to
+	 *      the first segment */
+	for (start_seg = text->data;
+	     (start_seg != NULL) && (start >= start_seg->length);
+	     start_seg = start_seg->next)
+	{
+		/* The subtraction is save, because this is,
+		 * what is tested for in the condition. */
+		start -= start_seg->length;
+
+		/* The variable end has also to be adjusted, because
+		 * otherwise we would have to start iterating at
+		 * the beginning again.
+		 * So this way it is more efficiently.
+		 *
+		 * The subtraction is save, because the end is always
+		 * greater than start and the last was save also. */
+		end -= start_seg->length;
+	}
+
+	/* The end of text was encountered while looking for
+	 * the start segment. Those the index is out of range. */
+	if (start_seg == NULL)
+	{
+		set_status (status, E_VALUE, 2,
+		            "SH_Text: index out of range.\n");
+
+		if (length != NULL)
+		{
+			*length = 0;
+		}
+
+		return NULL;
+	}
+
+	/* The requested string is fully inside one segment.
+	 * So set end_seg and len appropriately; end is already
+	 * relative to the first segment. */
+	if (end <= start_seg->length)
+	{
+		end_seg = start_seg;
+		len = end - start;
+	}
+
+	/* The requested string is in more than one segment.
+	 * Find the segment of the text, where the last part of
+	 * the string is contained and set end relative to it.
+	 * end_seg: The segment where the last part of the string
+	 *          is located.
+	 * end: The remaining length of the string inside
+	 *      this last segment.
+	 *
+	 * Also the absolute length is needed for allocating the string
+	 * and returning it. This would be the original value of
+	 * (end - start), but it can not be set directly, because end
+	 * could be point outside of the text. Those both has to be set
+	 * in each iteration over the text, till the last segment
+	 * is encountered (the end is smaller then the length of
+	 * the segment). */
+	else
+	{
+		/* skip the remaining of the start segment */
+		/* The subtraction is save, because start is relative
+		 * to the start segment (start < start_seg->length). */
+		len = start_seg->length - start;
+		/* The subtraction is save, because this is the
+		 * else branch. (end > start->length) */
+		end -= start_seg->length;
+
+		/* Iterate over the text till the end is inside
+		 * the current segment. Actually the comparison has
+		 * to be made against end_seg->next, because, if end
+		 * points outside of the text, a pointer to the last
+		 * segment has to be preserved. Otherwise we would
+		 * have to iterate over the text another time. */
+		for (end_seg = start_seg;
+		        (end_seg->next != NULL)
+		     && (end > end_seg->next->length);
+		     end_seg = end_seg->next)
+		{
+			/* The subtraction is save because this is,
+			 * what is tested for.
+			 * The addition is save, because len will be
+			 * equal or smaller then the original
+			 * (end - start)
+			 * and they is also of size_t and end > start.
+			 * Note that len = offset; is not possible
+			 * in the first place,
+			 * see the comment before the else branch. */
+			len += end_seg->next->length;
+			end -= end_seg->next->length;
+		}
+
+		/* We have encountered the end of text. Set end to
+		 * the length of the last segment (making it smaller);
+		 * end_seg already points to the last (non-NULL)
+		 * segment. */
+		if (end_seg->next == NULL)
+		{
+			end = end_seg->length;
+		}
+		/* The end of the requested string is inside the
+		 * next segment. The variable end is already
+		 * relative to it, so just add it to the length.
+		 * Also set end_seg to the next segment, this can't
+		 * be done earlier, because it may be NULL.
+		 * (See comment before if.) */
+		else
+		{
+			/* The addition is save, because len will be
+			 * equal or smaller then the original
+			 * (end - start)
+			 * and they is also of size_t and end > start.
+			 * Note that len = offset is not possible in
+			 * the first place, see the comment before
+			 * the outer else branch. */
+			len += end;
+			end_seg = end_seg->next;
+		}
+	}
+
+	/* Allocate the string to be returned. The multiplication is
+	 * save, as long as sizeof (char) == 1. (Should be ...) */
+	string = malloc ((len + 1) * sizeof (char));
+
+	/* Allocating has failed. Nothing has to be freed, because we
+	 * just have some stack vars and pointers to segments. */
+	if (string == NULL)
+	{
+		set_status (status, E_ALLOC, 6,
+		            "Memory allocation for SH_Text failed.\n");
+
+		if (length != NULL)
+		{
+			*length = 0;
+		}
+
+		return NULL;
+	}
+
+
+	/* add terminating NULL byte. */
+	string[0] = (char) 0;
+
+	/* The requested string is fully inside one segment. */
+	if (start_seg == end_seg)
+	{
+		strncat (string, start_seg->text + start, len);
+	}
+	/* The requested string is in more than one segment. */
+	else
+	{
+		/* copy part of the start segment */
+		strcat (string, start_seg->text + start);
+
+		/* copy all segments, that are contained in the
+		 * requested string fully */
+		for (start_seg = start_seg->next; start_seg != end_seg;
+		     start_seg = start_seg->next)
+		{
+			strcat (string, start_seg->text);
+		}
+
+		/* copy part of the last segment */
+		strncat (string, end_seg->text, end);
+	}
+
+
+	if (length != NULL)
+	{
+		*length = len;
+	}
+
+	set_success (status);
+
+	return string;
+}
+
+bool
+SH_Text_append_string (struct SH_Text * text, const char * string,
+                       /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@modifies text->data@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	size_t length;
+	size_t size;
+	char * new_text;
+	struct text_segment * seg;
+
+	length = strlen (string);
+
+	/* Fiddling around with more than one segment
+	 * would be to cumbersome. */
+	if (length == SIZE_MAX)
+	{
+		set_status (status, E_DOMAIN, 2,
+		            "Maximum length of SH_Text reached.\n");
+
+		return FALSE;
+	}
+
+	/* iterate to last segment
+	 * This can't be done in the end, because it is tried
+	 * to write the string to the last segment, if there
+	 * is still enough space. */
+	for (seg = text->data; seg->next != NULL; seg = seg->next);
+
+	/* The string fits at the end of the last segment. */
+	if (SIZE_MAX - seg->length > length)
+	{
+		length += seg->length;
+	}
+	/* It doesn't, so create a new segment after it. */
+	else
+	{
+		seg->next = malloc (sizeof (struct text_segment));
+
+		if (seg->next == NULL)
+		{
+			set_status (status, E_ALLOC, 4,
+			            "Memory allocation for "
+			            "SH_Text data failed.\n");
+
+/* useless assignment to silence splint */
+#ifdef S_SPLINT_S
+			seg->next = NULL;
+#endif
+
+			return FALSE;
+		}
+	}
+
+
+	/* Figure out how much memory has to be allocated
+	 * in multiplies of chunk size. If this would overflow,
+	 * just take the really needed size. */
+	/* If both operands were integers, this operation would fail
+	 * as the division will behave, like calling floor
+	 * in the first place. */
+	/* length + 1 is save, because at the last if
+	 * we tested for > instead of >= */
+	size = (size_t) ceilf ((float) (length+1) / (float) CHUNK_SIZE);
+	if (size > (SIZE_MAX / (float) CHUNK_SIZE))
+	{
+		size = length + 1;
+	}
+	else
+	{
+		size *= CHUNK_SIZE;
+	}
+
+	/* Enlarge the text. */
+	if (seg->next == NULL)
+	{
+		new_text = realloc (seg->text, size * sizeof (char));
+	}
+	/* If a new segment is made, there is also a new text */
+	else
+	{
+		new_text = malloc (size * sizeof (char));
+	}
+
+	/* Allocation failed */
+	if (new_text == NULL)
+	{
+		set_status (status, E_ALLOC,
+		            (seg->next == NULL) ? 11: 6,
+		            "Memory allocation for "
+		            "SH_Text data failed.\n");
+
+		/* Clean-up, if new segment was to be made. */
+		if (seg->next != NULL)
+		{
+			free (seg->next);
+			seg->next = NULL;
+		}
+
+/* useless assignment to silence splint */
+#ifdef S_SPLINT_S
+			seg->next = NULL;
+#endif
+
+		return FALSE;
+	}
+
+	/* If a new text is made, add the terminating NULL to
+	 * the string and switch to the new segment. */
+	if (seg->next != NULL)
+	{
+		new_text[0] = (char) 0;
+
+		seg = seg->next;
+		seg->next = NULL;
+	}
+
+	/* Save string and write changes */
+	seg->text = strcat (new_text, string);
+	seg->length = length;
+	seg->size = size;
+
+	set_success (status);
+
+	return TRUE;
+}
+
+bool
+SH_Text_append_text (struct SH_Text * text,
+                     const struct SH_Text * text2,
+                     /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@modifies text->data@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_Text * copy;
+	struct text_segment * seg;
+
+	copy = SH_Text_copy (text2, status);
+
+	if (copy == NULL)
+	{
+		return FALSE;
+	}
+
+	/* go to last segment */
+	for (seg = text->data; seg->next != NULL; seg = seg->next);
+
+	/* append */
+	seg->next = copy->data;
+
+	free (copy);
+
+	return TRUE;
+}
+
+void
+SH_Text_join (struct SH_Text * text,
+              /*@only@*/ struct SH_Text * text2)
+	/*@modifies text->data@*/
+	/*@modifies text2@*/
+	/*@releases text2@*/
+{
+	struct text_segment * seg;
+
+	/* go to last segment */
+	for (seg = text->data; seg->next != NULL; seg = seg->next);
+
+	/* append contents */
+	seg->next = text2->data;
+
+	/* destroy text2 */
+	free (text2);
+
+	return;
+}
+
+void
+SH_Text_print (const struct SH_Text * text)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+{
+	const struct text_segment * seg;
+
+	for (seg = text->data; seg != NULL; seg = seg->next)
+	{
+		printf ("%s", seg->text);
+	}
+
+	return;
+}
diff --git a/src/lib/sefht/text.h b/src/lib/sefht/text.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4bfa9632e63eb8adefa0773319a57c836c9dd4d
--- /dev/null
+++ b/src/lib/sefht/text.h
@@ -0,0 +1,141 @@
+/*
+ * text.h
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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_TEXT_H
+#define SEFHT_TEXT_H
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
+
+#include <stdbool.h>
+
+#include "status.h"
+
+
+typedef /*@abstract@*/ struct SH_Text SH_Text;
+
+
+/*@null@*/
+/*@only@*/
+SH_Text *
+SH_Text_new (/*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+/*@null@*/
+/*@only@*/
+SH_Text *
+SH_Text_new_from_string (const char * string,
+                         /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+void
+SH_Text_free (/*@only@*/ SH_Text * text)
+	/*@modifies text@*/
+	/*@releases text@*/;
+
+/*@null@*/
+/*@only@*/
+SH_Text *
+SH_Text_copy (const SH_Text * text,
+              /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+size_t
+SH_Text_get_length (const struct SH_Text * text,
+                    /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+/*@null@*/
+/*@only@*/
+char *
+SH_Text_get_char (const SH_Text * text,
+                  size_t index,
+                  /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+/*@null@*/
+/*@only@*/
+char *
+SH_Text_get_string (const SH_Text * text,
+                    size_t index, size_t offset,
+                    /*@null@*/ /*@out@*/ size_t * length,
+                    /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@modifies length@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+/*@null@*/
+/*@only@*/
+char *
+SH_Text_get_range (const SH_Text * text,
+                   size_t start, size_t end,
+                   /*@null@*/ /*@out@*/ size_t * length,
+                   /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@modifies length@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+bool
+SH_Text_append_string (SH_Text * text, const char * string,
+                       /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@modifies text@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+bool
+SH_Text_append_text (SH_Text * text,
+                     const SH_Text * text2,
+                     /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@modifies text@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+void
+SH_Text_join (SH_Text * text,
+              /*@only@*/ SH_Text * text2)
+	/*@modifies text@*/
+	/*@modifies text2@*/
+	/*@releases text2@*/;
+
+void
+SH_Text_print (const SH_Text * text)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/;
+
+#endif /* SEFHT_TEXT_H */
diff --git a/src/lib/sefht/validator.c b/src/lib/sefht/validator.c
new file mode 100644
index 0000000000000000000000000000000000000000..9c6ad8ce01b8e40816f7518757dcff4067447b93
--- /dev/null
+++ b/src/lib/sefht/validator.c
@@ -0,0 +1,131 @@
+/*
+ * validator.c
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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 <stdlib.h>
+
+#include "log.h"
+#include "status.h"
+
+#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"
+struct SH_Validator
+{
+	TAG_DATA
+};
+
+#define VALIDATOR_IS_DEFINED
+#include "validator_tag.c"
+
+
+/*@null@*/
+/*@only@*/
+struct SH_Validator *
+SH_Validator_new (/*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_Validator * validator;
+	validator = malloc (sizeof (struct SH_Validator));
+
+	if (validator == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for "
+		            "SH_Validator failed.\n");
+
+		return NULL;
+	}
+
+	if (!init_tags (validator, status))
+	{
+/* dangerous call to silence splint, should never be executed. */
+#ifdef S_SPLINT_S
+		free (validator->tags);
+#endif
+		free (validator);
+		return NULL;
+	}
+
+	set_success (status);
+
+	return validator;
+}
+
+/*@null@*/
+/*@only@*/
+struct SH_Validator *
+SH_Validator_copy (const struct SH_Validator * validator,
+                   /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_Validator * copy;
+	copy = malloc (sizeof (struct SH_Validator));
+
+	if (copy == NULL)
+	{
+		set_status (status, E_ALLOC, 4,
+		            "Memory allocation for "
+		            "SH_Validator failed.\n");
+
+		return NULL;
+	}
+
+	if (!copy_tags (copy, validator, status))
+	{
+/* dangerous call to silence splint, should never be executed. */
+#ifdef S_SPLINT_S
+		free (copy->tags);
+#endif
+		free (copy);
+		return NULL;
+	}
+
+	set_success (status);
+
+	return copy;
+}
+
+void
+SH_Validator_free (/*@only@*/ struct SH_Validator * validator)
+	/*@modifies validator@*/
+	/*@releases validator@*/
+{
+	free_tags (validator);
+	free (validator);
+
+	return;
+}
diff --git a/src/lib/sefht/validator.h b/src/lib/sefht/validator.h
new file mode 100644
index 0000000000000000000000000000000000000000..55de52ab711b8b9957d4d5e3d8ca326c2e9348d4
--- /dev/null
+++ b/src/lib/sefht/validator.h
@@ -0,0 +1,65 @@
+/*
+ * validator.h
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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_H
+#define SEFHT_VALIDATOR_H
+
+#if !defined (SEFHT_SEFHT_H_INSIDE) && !defined (SEFHT_COMPILATION)
+#error "Only <sefht/sefht.h> can be included directly."
+#endif
+
+#include "status.h"
+
+
+typedef struct SH_Validator SH_Validator;
+
+#define __VALIDATOR_H_INSIDE__
+#include "validator_tag.h"
+#undef __VALIDATOR_H_INSIDE__
+
+
+/*@null@*/
+/*@only@*/
+SH_Validator *
+SH_Validator_new (/*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+/*@null@*/
+/*@only@*/
+SH_Validator *
+SH_Validator_copy (const SH_Validator * validator,
+                   /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+void
+SH_Validator_free (/*@only@*/ struct SH_Validator * validator)
+	/*@modifies validator@*/
+	/*@releases validator@*/;
+
+
+#endif /* SEFHT_VALIDATOR_H */
diff --git a/src/lib/sefht/validator_tag.c b/src/lib/sefht/validator_tag.c
new file mode 100644
index 0000000000000000000000000000000000000000..887b996c54ce188f6acc159ab5f08ab76552b447
--- /dev/null
+++ b/src/lib/sefht/validator_tag.c
@@ -0,0 +1,663 @@
+/*
+ * validator_tag.c
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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_tag.h"
+
+
+#ifndef VALIDATOR_IS_DEFINED
+
+#define NEXT_TAG(tag) (tag + 1)
+
+struct tag_info
+{
+	union
+	{
+		struct
+		{
+			Tag id;
+			/*@only@*/ char * name;
+		} data;
+		size_t next;
+	};
+};
+
+#define TAG_DATA                                                       \
+	/*@only@*/ struct tag_info * tags;                             \
+	size_t tag_n;                                                  \
+	Tag last_tag;                                                  \
+
+#else /* VALIDATOR_IS_DEFINED */
+
+static inline
+bool
+init_tags (/*@special@*/ struct SH_Validator * validator,
+           /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@defines validator->tags,
+	        validator->tag_n,
+	        validator->last_tag@*/
+	/*@modifies validator->tags@*/
+	/*@modifies validator->tag_n@*/
+	/*@modifies validator->last_tag@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+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@*/
+	/*@modifies copy->tags@*/
+	/*@modifies copy->tag_n@*/
+	/*@modifies copy->last_tag@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+static inline
+void
+free_tags (/*@special@*/ struct SH_Validator * validator)
+	/*@modifies validator->tags@*/
+	/*@releases validator->tags@*/;
+
+static inline
+size_t
+get_tag_number (const struct SH_Validator * validator)
+	/*@*/;
+
+static inline
+Tag
+add_tag (struct SH_Validator * validator,
+         const char * tag,
+         /*@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)
+	/*@*/;
+
+static inline
+bool
+is_tag_name (const struct SH_Validator * validator, const char * name)
+	/*@*/;
+
+/*@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@*/;
+
+static inline
+Tag
+get_tag_id_by_name (const struct SH_Validator * validator,
+                    const char * name)
+	/*@*/;
+
+static inline
+bool
+remove_tag (struct SH_Validator * validator, Tag id,
+            /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@modifies validator->tag_n@*/
+	/*@modifies validator->tags@*/
+	/*@modifies validator->last_tag@*/
+	/*@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)
+
+
+static inline
+bool
+init_tags (/*@special@*/ struct SH_Validator * validator,
+           /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@defines validator->tags,
+	           validator->tag_n,
+	           validator->last_tag@*/
+	/*@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)
+	{
+		set_status (status, E_ALLOC, 3, "malloc failed");
+
+		validator->tag_n = 0;
+		validator->last_tag = TAG_ERR;
+		return FALSE;
+	}
+
+	validator->tags[0].next = 0;
+	validator->tag_n = 0;
+	validator->last_tag = TAG_ERR;
+
+	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@*/
+	/*@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));
+
+	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++)
+	{
+		/* 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)
+		{
+			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++;
+		}
+	}
+
+	return TRUE;
+}
+
+static inline
+void
+free_tags (/*@special@*/ struct SH_Validator * validator)
+	/*@modifies validator->tags@*/
+	/*@releases validator->tags@*/
+{
+	EXECUTE_ON_ALL_TAGS (
+	    validator->tag,
+	{
+		free (validator->tags[index].data.name);
+	});
+
+	free (validator->tags);
+	return;
+}
+
+static inline
+size_t
+get_tag_number (const struct SH_Validator * validator)
+	/*@*/
+{
+	size_t tag_n;
+
+	tag_n = 0;
+
+	EXECUTE_ON_ALL_TAGS (
+	    validator->tag,
+	{
+		/* This addition is always save,
+		 * because tag_n is always smaller than
+		 * validator->tag_n and it is also of size_t. */
+		tag_n++;
+	});
+
+	return tag_n;
+}
+
+static inline
+Tag
+add_tag (struct SH_Validator * validator,
+         const char * tag,
+         /*@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;
+
+	/* 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))))))
+	{
+		set_status (status, E_DOMAIN, 2,
+		            "maximum number of tags reached");
+		return TAG_ERR;
+	}
+
+	if (validator->tags[0].next == 0)
+	/* allocate new space */
+	{
+		struct tag_info * new_tags;
+
+		/* 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));
+
+		if (new_tags == NULL)
+		{
+			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;
+#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;
+	}
+
+	tag_id = NEXT_TAG (validator->last_tag);
+	validator->tags[index].data.id = tag_id;
+	validator->tags[index].data.name = strdup (tag);
+
+	if (validator->tags[index].data.name == NULL)
+	{
+		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;
+	}
+
+	/* commit changes */
+	if (is_new)
+	{
+		validator->tag_n++;
+	}
+
+	validator->last_tag = tag_id;
+
+	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;
+}
+
+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)
+	/*@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;
+}
+
+static inline
+bool
+remove_tag (struct SH_Validator * validator, Tag id,
+            /*@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);
+
+		if (index == tag_n)
+		{
+			struct tag_info * new_tags;
+
+			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");
+			}
+
+			new_tags = realloc (tags,
+			                    sizeof (struct tag_info)
+			                    * index);
+
+			if (new_tags == NULL)
+			{
+				set_status (status, E_ALLOC, 4,
+				            "realloc failed");
+
+/* bad code to silence splint, should never be executed. */
+#ifdef S_SPLINT_S
+				tags = (void *) 0x12345;
+#endif
+				return FALSE;
+			}
+
+			tags = new_tags;
+		}
+		else
+		{
+			tags[index].next = tags[0].next;
+			tags[0].next = index;
+		}
+
+		set_success (status);
+		return TRUE;
+	});
+	#undef tags
+	#undef tag_n
+
+	set_status (status, E_VALUE, 68, "unknown Tag id");
+	return FALSE;
+}
+
+
+bool
+SH_Validator_check_tag (struct SH_Validator * validator,
+                        const char * tag)
+	/*@*/
+{
+	return is_tag_name (validator, tag);
+}
+
+Tag
+/*@alt void@*/
+SH_Validator_register_tag (struct SH_Validator * validator,
+                           const char * tag,
+                           /*@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;
+
+	/* tag already registered */
+	tag_id = get_tag_id_by_name (validator, tag);
+	if (tag_id != TAG_ERR)
+	{
+		return tag_id;
+	}
+
+	return add_tag (validator, tag, status);
+}
+
+bool
+SH_Validator_deregister_tag (struct SH_Validator * validator,
+                             Tag id,
+                             /*@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);
+}
+
+#endif /* VALIDATOR_IS_DEFINED */
diff --git a/src/lib/sefht/validator_tag.h b/src/lib/sefht/validator_tag.h
new file mode 100644
index 0000000000000000000000000000000000000000..185ff88b2e250e64c222d4e9ef62598115450bdc
--- /dev/null
+++ b/src/lib/sefht/validator_tag.h
@@ -0,0 +1,70 @@
+/*
+ * validator_tag.h
+ *
+ * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
+ *
+ * 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_H
+#define SEFHT_VALIDATOR_TAG_H
+
+#if !defined (SEFHT_VALIDATOR_H)
+#error "Please include only <sefht/validator.h>."
+#endif
+
+#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
+
+
+Tag
+/*@alt void@*/
+SH_Validator_register_tag (SH_Validator * validator,
+                           const char * tag,
+                           /*@null@*/ /*@out@*/
+                           struct SH_Status * status)
+	/*@modifies validator@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+bool
+SH_Validator_deregister_tag (SH_Validator * validator,
+                             Tag id,
+                             /*@null@*/ /*@out@*/
+                             struct SH_Status * status)
+	/*@modifies validator@*/
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
+bool
+SH_Validator_check_tag (struct SH_Validator * validator,
+                        const char * tag)
+	/*@*/;
+
+#endif /* SEFHT_VALIDATOR_TAG_H */
diff --git a/src/main.c b/src/main.c
index 95c0d761ae5dc9f61e1cbe7dd9ec51308ccccea6..c869541b2bbe466d5a96aea7c6e45e4a22208490 100644
--- a/src/main.c
+++ b/src/main.c
@@ -23,32 +23,33 @@
 
 
 #include <stdio.h>
-#include "cms.h"
-#include "data.h"
-#include "fragment.h"
-#include "node_fragment.h"
-#include "text.h"
+#include <stdlib.h>
+
+#include <sefht/sefht.h>
 
 int main(int argc, char **argv)
 {
 	page_t page;
 
 	/* startup */
-	struct SH_Cms * cms = SH_Cms_new (NULL);
+	SH_Cms * cms = SH_Cms_new (NULL);
 	/* startup */
 
 	page = SH_Cms_register_page (cms, "Startpage", NULL);
 
 	/* shutdown */
-	SH_Cms_free (cms, NULL);
+	SH_Cms_free (cms);
 	/* shutdown */
 
+	(void) argc;
+	(void) argv;
+	(void) page;
 
-	struct SH_Data * data;
-	struct SH_Fragment * root;
-	struct SH_Fragment * child1;
-	struct SH_Fragment * child2;
-	struct SH_Text * text;
+	SH_Data * data;
+	SH_Fragment * root;
+	SH_Fragment * child1;
+	SH_Fragment * child2;
+	SH_Text * text;
 
 	data = SH_Data_new (NULL);
 
@@ -80,20 +81,20 @@ int main(int argc, char **argv)
 				      child2, NULL);
 
 	text = SH_Fragment_to_html (root, WRAP, 0, 1, INDENT_TEXT, NULL);
+	SH_Text_print (text);
+	printf ("\n");
 
-	printf ("%s\n", text->text);
-
-	SH_Text_free (text, NULL);
+	SH_Text_free (text);
 
 	text = SH_Fragment_to_html (root, INLINE, 0, 1, INDENT_TEXT, NULL);
+	SH_Text_print (text);
+	printf ("\n");
 
-	printf ("%s\n", text->text);
-
-	SH_Text_free (text, NULL);
+	SH_Text_free (text);
 
-	SH_Fragment_free (root, NULL);
+	SH_Fragment_free (root);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 
 	return 0;
 }
diff --git a/src/node_fragment.c b/src/node_fragment.c
deleted file mode 100644
index 58d93ff392a1fda5e2bb47496fa6078693ef71db..0000000000000000000000000000000000000000
--- a/src/node_fragment.c
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * node_fragment.c
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 <errno.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "macro.h"
-#include "error.h"
-#include "log.h"
-
-#include "data.h"
-#include "text.h"
-#include "validator.h"
-
-#include "fragment.h"
-
-#include "node_fragment.h"
-
-
-static const struct fragment_methods methods = {
-	(struct SH_Fragment * (*)(struct SH_Fragment *,
-				  struct SH_Error *))
-	SH_NodeFragment_deepcopy,
-
-	(void (*)(struct SH_Fragment *, struct SH_Error *))
-	SH_NodeFragment_free,
-
-	(struct SH_Text * (*)(struct SH_Fragment *, enum HTML_MODE,
-			      unsigned int, unsigned int, const char *,
-			      struct SH_Error *))
-	SH_NodeFragment_to_html
-};
-
-
-struct SH_Fragment *
-SH_NodeFragment_new (const char * tag, struct SH_Data * data,
-		     struct SH_Error * error)
-{
-	struct SH_NodeFragment * fragment;
-
-	if (!SH_Validator_check_tag (data->validator, tag))
-	{
-		ERROR2 ("Tag %s is'nt valid.\n", tag);
-
-		if (error != NULL)
-		{
-			error->type = VALUE_ERROR;
-		}
-
-		return NULL;
-	}
-
-	fragment = malloc (sizeof (struct SH_NodeFragment));
-	if (fragment == NULL)
-	{
-		ERROR1 ("Memory allocation for "
-			"SH_NodeFragment failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return NULL;
-	}
-
-	init_fragment (&(fragment->base), data, NODE, &methods);
-
-	fragment->tag = strdup (tag);
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for fragment tag failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		free (fragment);
-
-		return NULL;
-	}
-
-
-	fragment->child_n = 0;
-	fragment->childs = malloc (0);
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return (struct SH_Fragment *) fragment;
-}
-
-void
-SH_NodeFragment_free (struct SH_NodeFragment * fragment,
-		      struct SH_Error * error)
-{
-	size_t index;
-
-	free (fragment->tag);
-
-	for (index = 0; index < fragment->child_n; index++)
-	{
-		SH_Fragment_free (fragment->childs[index], error);
-	}
-	free (fragment->childs);
-
-	free_fragment (&fragment->base);
-
-	free (fragment);
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return;
-}
-
-struct SH_Fragment *
-SH_NodeFragment_copy (struct SH_NodeFragment * fragment,
-		      struct SH_Error * error)
-{
-	struct SH_NodeFragment * copy;
-
-	copy = malloc (sizeof (struct SH_NodeFragment));
-	if (copy == NULL)
-	{
-		ERROR1 ("Memory allocation for "
-			"SH_NodeFragment failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return NULL;
-	}
-
-	copy_fragment (&(copy->base), &(fragment->base));
-
-	copy->tag = strdup (fragment->tag);
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for fragment tag failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		free (copy);
-
-		return NULL;
-	}
-
-
-	copy->child_n = 0;
-	copy->childs = malloc (0);
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return (struct SH_Fragment *) copy;
-}
-
-struct SH_Fragment *
-SH_NodeFragment_deepcopy (struct SH_NodeFragment * fragment,
-		          struct SH_Error * error)
-{
-	struct SH_NodeFragment * copy;
-	struct SH_Fragment * child;
-	size_t index;
-
-	copy = malloc (sizeof (struct SH_NodeFragment));
-	if (copy == NULL)
-	{
-		ERROR1 ("Memory allocation for "
-			"SH_NodeFragment failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return NULL;
-	}
-
-	copy_fragment (&(copy->base), &(fragment->base));
-
-	copy->tag = strdup (fragment->tag);
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for fragment tag failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		free (copy);
-
-		return NULL;
-	}
-
-
-	copy->child_n = fragment->child_n;
-	copy->childs = malloc (sizeof (struct SH_NodeFragment *)
-			       * fragment->child_n);
-
-	if (copy->child_n != 0 && copy->childs == NULL)
-	{
-		ERROR1 ("Memory allocation for fragment child failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		free (copy->tag);
-		free (copy);
-
-		return NULL;
-	}
-
-	for (index = 0; index < fragment->child_n; index++)
-	{
-		child = SH_Fragment_copy (fragment->childs[index],
-					  error);
-
-		if (child == NULL)
-		{
-			copy->child_n = index;
-			SH_NodeFragment_free (copy, NULL);
-
-			return NULL;
-		}
-
-		copy->childs[index] = child;
-	}
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return (struct SH_Fragment *) copy;
-}
-
-bool
-SH_Fragment_is_NodeFragment (struct SH_Fragment * fragment)
-{
-	return get_type (fragment) == NODE;
-}
-
-char *
-SH_NodeFragment_get_tag (struct SH_NodeFragment * fragment,
-			 struct SH_Error * error)
-{
-	char * tag;
-
-	tag = strdup (fragment->tag);
-
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for fragment tag failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return NULL;
-	}
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return tag;
-}
-
-struct SH_Fragment *
-SH_NodeFragment_get_child (struct SH_NodeFragment * fragment, size_t index,
-			   struct SH_Error * error)
-{
-	if (index >= fragment->child_n)
-	{
-		ERROR1 ("Fragment: Child index out of range.\n");
-
-		if (error != NULL)
-		{
-			error->type = VALUE_ERROR;
-		}
-
-		return NULL;
-	}
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return fragment->childs[index];
-}
-
-bool
-SH_NodeFragment_is_child (struct SH_NodeFragment * fragment,
-			  struct SH_Fragment * child)
-{
-	size_t index;
-
-	for (index = 0; index < fragment->child_n; index++)
-	{
-		if (fragment->childs[index] == child)
-		{
-			return TRUE;
-		}
-	}
-	return FALSE;
-}
-
-bool
-SH_NodeFragment_is_descendant (struct SH_NodeFragment * fragment,
-			       struct SH_Fragment * child)
-{
-	size_t index;
-
-	for (index = 0; index < fragment->child_n; index++)
-	{
-		if (fragment->childs[index] == child
-		|| (SH_Fragment_is_NodeFragment (child)
-		    && SH_NodeFragment_is_descendant (
-					(struct SH_NodeFragment *)
-					fragment->childs[index],
-					child)))
-		{
-			return TRUE;
-		}
-	}
-	return FALSE;
-}
-
-bool
-SH_NodeFragment_append_child (struct SH_NodeFragment * fragment,
-			      struct SH_Fragment * child,
-			      struct SH_Error * error)
-{
-	fragment->childs = realloc (fragment->childs,
-				      sizeof (struct SH_Fragment *)
-				      * (fragment->child_n + 1));
-
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for fragment child failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return FALSE;
-	}
-
-	fragment->childs[fragment->child_n] = child;
-	fragment->child_n++;
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return TRUE;
-}
-
-struct SH_Text *
-SH_NodeFragment_to_html (struct SH_NodeFragment * fragment,
-			 enum HTML_MODE mode,
-			 unsigned int indent_base,
-			 unsigned int indent_step,
-			 char * indent_char,
-			 struct SH_Error * error)
-{
-	struct SH_Text * html;
-	struct SH_Text * child;
-	struct SH_Text * indent_text;
-	size_t index;
-
-	html = SH_Text_new (error);
-	if (error != NULL && error->type != SUCCESS)
-	{
-		return NULL;
-	}
-
-	if (mode == WRAP)
-	{
-		indent_text = SH_Text_new (error);
-		if (indent_text == NULL)
-		{
-			SH_Text_free (html, NULL);
-			return NULL;
-		}
-
-		for (index = 0; index < indent_base; index++)
-		{
-			if (!SH_Text_append_string (indent_text,
-						    indent_char, error))
-			{
-				SH_Text_free (html, NULL);
-				SH_Text_free (indent_text, NULL);
-				return NULL;
-			}
-		}
-
-		if (!SH_Text_append_text (html, indent_text, error))
-		{
-			SH_Text_free (html, NULL);
-			SH_Text_free (indent_text, NULL);
-			return NULL;
-		}
-	}
-
-	if (!SH_Text_append_string (html, OPEN_TAG_BEGIN, error))
-	{
-		SH_Text_free (html, NULL);
-
-		if (mode == WRAP)
-		{
-			SH_Text_free (indent_text, NULL);
-		}
-
-		return NULL;
-	}
-
-	if (!SH_Text_append_string (html, fragment->tag, error))
-	{
-		SH_Text_free (html, NULL);
-
-		if (mode == WRAP)
-		{
-			SH_Text_free (indent_text, NULL);
-		}
-
-		return NULL;
-	}
-
-	if (!SH_Text_append_string (html, OPEN_TAG_END, error))
-	{
-		SH_Text_free (html, NULL);
-
-		if (mode == WRAP)
-		{
-			SH_Text_free (indent_text, NULL);
-		}
-
-		return NULL;
-	}
-
-	if (mode == WRAP)
-	{
-		if (!SH_Text_append_string (html, NEWLINE, error))
-		{
-			SH_Text_free (html, NULL);
-			SH_Text_free (indent_text, NULL);
-			return NULL;
-		}
-	}
-
-	for (index = 0; index < fragment->child_n; index++)
-	{
-		child = SH_Fragment_to_html (fragment->childs[index],
-					     mode,
-					     indent_base + indent_step,
-					     indent_step,
-					     indent_char,
-					     error);
-
-		if (child == NULL)
-		{
-			SH_Text_free (html, NULL);
-
-			if (mode == WRAP)
-			{
-				SH_Text_free (indent_text, NULL);
-			}
-
-			return NULL;
-		}
-
-		if (!SH_Text_join (html, child, error))
-		{
-			SH_Text_free (html, NULL);
-			SH_Text_free (child, NULL);
-
-			if (mode == WRAP)
-			{
-				SH_Text_free (indent_text, NULL);
-			}
-
-			return NULL;
-		}
-	}
-
-	if (mode == WRAP)
-	{
-		if (!SH_Text_append_text (html, indent_text, error))
-		{
-			SH_Text_free (html, NULL);
-			SH_Text_free (indent_text, NULL);
-			return NULL;
-		}
-	}
-
-	if (!SH_Text_append_string (html, CLOSE_TAG_BEGIN, error))
-	{
-		SH_Text_free (html, NULL);
-
-		if (mode == WRAP)
-		{
-			SH_Text_free (indent_text, NULL);
-		}
-
-		return NULL;
-	}
-
-	if (!SH_Text_append_string (html, fragment->tag, error))
-	{
-		SH_Text_free (html, NULL);
-
-		if (mode == WRAP)
-		{
-			SH_Text_free (indent_text, NULL);
-		}
-
-		return NULL;
-	}
-
-	if (!SH_Text_append_string (html, CLOSE_TAG_END, error))
-	{
-		SH_Text_free (html, NULL);
-
-		if (mode == WRAP)
-		{
-			SH_Text_free (indent_text, NULL);
-		}
-
-		return NULL;
-	}
-
-	if (mode == WRAP)
-	{
-		if (!SH_Text_append_string (html, NEWLINE, error))
-		{
-			SH_Text_free (html, NULL);
-			SH_Text_free (indent_text, NULL);
-			return NULL;
-		}
-
-		SH_Text_free (indent_text, NULL);
-	}
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return html;
-}
diff --git a/src/node_fragment.h b/src/node_fragment.h
deleted file mode 100644
index fecfafe6f108d1163d92126f2abcf2c9b614df82..0000000000000000000000000000000000000000
--- a/src/node_fragment.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * node_fragment.h
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 _NODE_FRAGMENT_H
-#define _NODE_FRAGMENT_H
-
-#include "data.h"
-#include "text.h"
-
-#include "fragment.h"
-
-
-struct SH_NodeFragment;
-struct SH_NodeFragment
-{
-	struct SH_Fragment base;
-
-	char * tag;
-
-	size_t child_n;
-	struct SH_Fragment ** childs;
-};
-
-#define OPEN_TAG_BEGIN "<"
-#define OPEN_TAG_END ">"
-#define CLOSE_TAG_BEGIN "</"
-#define CLOSE_TAG_END ">"
-
-#define INDENT_TEXT "\t"
-#define NEWLINE "\n"
-
-struct SH_Fragment *
-SH_NodeFragment_new (const char * tag,
-		     struct SH_Data * data,
-		     struct SH_Error * error);
-
-void
-SH_NodeFragment_free (struct SH_NodeFragment * fragment,
-		      struct SH_Error * error);
-
-struct SH_Fragment *
-SH_NodeFragment_copy (struct SH_NodeFragment * fragment,
-		      struct SH_Error * error);
-
-struct SH_Fragment *
-SH_NodeFragment_deepcopy (struct SH_NodeFragment * fragment,
-			  struct SH_Error * error);
-
-bool
-SH_Fragment_is_NodeFragment (struct SH_Fragment * fragment);
-
-char *
-SH_NodeFragment_get_tag (struct SH_NodeFragment * fragment,
-			 struct SH_Error * error);
-
-struct SH_Fragment *
-SH_NodeFragment_get_child (struct SH_NodeFragment * fragment,
-			   size_t index,
-			   struct SH_Error * error);
-
-bool
-SH_NodeFragment_is_child (struct SH_NodeFragment * fragment,
-			  struct SH_Fragment * child);
-
-bool
-SH_NodeFragment_is_descendant (struct SH_NodeFragment * fragment,
-			       struct SH_Fragment * child);
-
-bool
-SH_NodeFragment_append_child (struct SH_NodeFragment * fragment,
-			      struct SH_Fragment * child,
-			      struct SH_Error * error);
-
-struct SH_Text *
-SH_NodeFragment_to_html (struct SH_NodeFragment * fragment,
-			 enum HTML_MODE mode,
-			 unsigned int indent_base,
-			 unsigned int indent_step,
-			 char * indent_char,
-			 struct SH_Error * error);
-
-#endif /* _NODE_FRAGMENT_H */
diff --git a/src/text.c b/src/text.c
deleted file mode 100644
index 33d88cf0e6948a4f0201135feb22b66fad9c69c1..0000000000000000000000000000000000000000
--- a/src/text.c
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * text.c
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 <errno.h>
-#include <math.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "macro.h"
-#include "error.h"
-#include "log.h"
-
-#include "text.h"
-
-
-struct SH_Text *
-SH_Text_new (struct SH_Error * error)
-{
-	struct SH_Text * text;
-	text = malloc (sizeof (struct SH_Text));
-
-	if (text == NULL)
-	{
-		ERROR1 ("Memory allocation for SH_Text failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return NULL;
-	}
-
-	text->length = 0;
-	text->text = malloc (CHUNK_SIZE * sizeof (char));
-
-	if (text->text == NULL)
-	{
-		ERROR1 ("Memory allocation for SH_Text failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		free (text);
-
-		return NULL;
-	}
-
-	text->text[0] = 0;
-	text->size = CHUNK_SIZE;
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return text;
-}
-
-void
-SH_Text_free (struct SH_Text * text, struct SH_Error * error)
-{
-	free (text->text);
-
-	free (text);
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return;
-}
-
-bool
-SH_Text_enlarge (struct SH_Text * text, size_t size,
-		 struct SH_Error * error)
-{
-	size_t new_size;
-
-	if (size <= text->size)
-	{
-		if (error != NULL)
-		{
-			error->type = SUCCESS;
-		}
-		return TRUE;
-	}
-
-	/* if both operands were integers,
-	 * this operation would fail
-	 * as the division will behave like
-	 * calling floor in the first place. */
-	new_size = (size_t) ceilf ((float) size / (float) CHUNK_SIZE);
-	if (new_size >= (SIZE_MAX / (float) CHUNK_SIZE))
-	{
-		ERROR1 ("Maximum length of SH_Text reached.\n");
-
-		if (error != NULL)
-		{
-			error->type = DOMAIN_ERROR;
-		}
-
-		return FALSE;
-	}
-
-	new_size *= CHUNK_SIZE;
-	text->text = realloc (text->text, new_size * sizeof (char));
-
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for SH_Text data failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return FALSE;
-	}
-
-	text->size = new_size;
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return TRUE;
-};
-
-bool
-SH_Text_append_string (struct SH_Text * text, char * string,
-		       struct SH_Error * error)
-{
-	size_t length;
-
-	length = strlen (string);
-
-	if (((SIZE_MAX - length) == 0)
-	|| ((SIZE_MAX - length - 1) < text->length))
-	{
-		ERROR1 ("Maximum length of SH_Text reached.\n");
-
-		if (error != NULL)
-		{
-			error->type = DOMAIN_ERROR;
-		}
-
-		return FALSE;
-	}
-
-	if (!SH_Text_enlarge (text, text->length + length + 1, error))
-	{
-		return FALSE;
-	}
-
-	text->text = strcat (text->text, string);
-
-	/* this is save as text->size is always at least one greater
-	 * and SH_Text_enlarge hasn't failed. */
-	text->length += length;
-
-	return TRUE;
-}
-
-bool
-SH_Text_append_text (struct SH_Text * text, struct SH_Text * text2,
-		     struct SH_Error * error)
-{
-	if (((SIZE_MAX - text->length) == 0)
-	|| ((SIZE_MAX - text->length - 1) < text2->length))
-	{
-		ERROR1 ("Maximum length of SH_Text reached.\n");
-
-		if (error != NULL)
-		{
-			error->type = DOMAIN_ERROR;
-		}
-
-		return FALSE;
-	}
-
-	if (!SH_Text_enlarge (text, text->length + text2->length + 1,
-			      error))
-	{
-		return FALSE;
-	}
-
-	text->text = strcat (text->text, text2->text);
-
-	/* this is save as text->size is always at least one greater
-	 * and SH_Text_enlarge hasn't failed. */
-	text->length += text2->length;
-
-	return TRUE;
-}
-
-bool
-SH_Text_join (struct SH_Text * text, struct SH_Text * text2,
-	      struct SH_Error * error)
-{
-	if (!SH_Text_append_text (text, text2, error))
-	{
-		return FALSE;
-	}
-
-	SH_Text_free (text2, error);
-
-	return TRUE;
-}
diff --git a/src/text.h b/src/text.h
deleted file mode 100644
index 8c6bfb6041cbd2cafc4efe1cb21ff5589d4dde2a..0000000000000000000000000000000000000000
--- a/src/text.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * text.h
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 _TEXT_H
-#define _TEXT_H
-
-#include <stdbool.h>
-
-#include "error.h"
-
-
-#define CHUNK_SIZE 64
-
-struct SH_Text
-{
-	size_t length;
-	size_t size;
-	char * text;
-};
-
-struct SH_Text * SH_Text_new (struct SH_Error * error);
-void SH_Text_free (struct SH_Text * text, struct SH_Error * error);
-
-bool SH_Text_enlarge (struct SH_Text * text, size_t size,
-		      struct SH_Error * error);
-
-bool SH_Text_append_string (struct SH_Text * text, char * string,
-			    struct SH_Error * error);
-
-bool SH_Text_append_text (struct SH_Text * text, struct SH_Text * text2,
-			  struct SH_Error * error);
-
-bool SH_Text_join (struct SH_Text * text, struct SH_Text * text2,
-		   struct SH_Error * error);
-
-#endif /* _TEXT_H */
diff --git a/src/validator.c b/src/validator.c
deleted file mode 100644
index c52c2554bd86fb73bff90983c76fd334bdbabb56..0000000000000000000000000000000000000000
--- a/src/validator.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * validator.c
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 <errno.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "macro.h"
-#include "error.h"
-#include "log.h"
-
-#include "validator.h"
-
-
-static inline void init_tags (struct SH_Validator * validator);
-
-struct SH_Validator *
-SH_Validator_new (struct SH_Error * error)
-{
-	struct SH_Validator * validator;
-	validator = malloc (sizeof (struct SH_Validator));
-
-	if (validator == NULL)
-	{
-		ERROR1 ("Memory allocation for SH_Validator failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return NULL;
-	}
-
-	init_tags (validator);
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return validator;
-}
-
-static inline void free_tags (struct SH_Validator * validator);
-
-void
-SH_Validator_free (struct SH_Validator * validator,
-		   struct SH_Error * error)
-{
-	free_tags (validator);
-	free (validator);
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return;
-}
-
-static inline void
-init_tags (struct SH_Validator * validator)
-{
-	validator->tag_n = 0;
-	validator->tags = malloc (0);
-	validator->last_tag = TAG_ERR;
-
-	return;
-}
-
-static inline void
-free_tags (struct SH_Validator * validator)
-{
-	unsigned int index;
-
-	for (index = 0; index < validator->tag_n; index++)
-	{
-		free (validator->tags[index].name);
-	}
-
-	free (validator->tags);
-	return;
-}
-
-
-static inline tag_t
-SH_Validator_get_tag (struct SH_Validator * validator, const char * tag)
-{
-	unsigned int index;
-
-	for (index = 0; index < validator->tag_n; index++)
-	{
-		if (strcmp (validator->tags[index].name, tag) == 0)
-		{
-			return validator->tags[index].id;
-		}
-	}
-
-	return TAG_ERR;
-}
-
-bool
-SH_Validator_check_tag (struct SH_Validator * validator,
-			const char * tag)
-{
-	if (SH_Validator_get_tag (validator, tag) == TAG_ERR)
-	{
-		return FALSE;
-	}
-	else
-	{
-		return TRUE;
-	}
-}
-
-tag_t
-SH_Validator_register_tag (struct SH_Validator * validator,
-			   const char * tag, struct SH_Error * error)
-{
-	tag_t tag_id;
-
-	/* tag already registered */
-	tag_id = SH_Validator_get_tag (validator, tag);
-	if (tag_id != TAG_ERR)
-	{
-		return tag_id;
-	}
-
-	/* abort on overflow */
-	if (validator->tag_n == UINT_MAX
-	|| validator->last_tag == TAG_MAX)
-	{
-		ERROR1 ("Maximum number of tags reached.\n");
-
-		if (error != NULL)
-		{
-			error->type = DOMAIN_ERROR;
-		}
-
-		return TAG_ERR;
-	}
-
-	validator->tags = realloc (validator->tags,
-			(validator->tag_n + 1) * sizeof (struct SH_Tag));
-
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for tag failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return TAG_ERR;
-	}
-
-	NEXT_TAG(validator->last_tag);
-
-	validator->tags[validator->tag_n].id = validator->last_tag;
-	validator->tags[validator->tag_n].name = strdup (tag);
-
-	if (errno == ENOMEM)
-	{
-		ERROR1 ("Memory allocation for tag failed.\n");
-
-		if (error != NULL)
-		{
-			error->type = ALLOCATION_FAILED;
-		}
-
-		return TAG_ERR;
-	}
-
-	validator->tag_n++;
-
-	if (error != NULL)
-	{
-		error->type = SUCCESS;
-	}
-
-	return validator->last_tag;
-}
diff --git a/src/validator.h b/src/validator.h
deleted file mode 100644
index f0fd7beca52d4dd695271471c1bf1dfeb547ccc3..0000000000000000000000000000000000000000
--- a/src/validator.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * validator.h
- *
- * Copyright 2022 Jonathan Schöbel <jonathan@Ubermos-2019>
- *
- * 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 _VALIDATOR_H
-#define _VALIDATOR_H
-
-#include <limits.h>
-#include <stdbool.h>
-
-
-typedef unsigned int tag_t;
-#define TAG_ERR 0
-#define TAG_MIN 1
-#define TAG_MAX UINT_MAX
-#define NEXT_TAG(tag) tag++
-
-struct SH_Tag
-{
-	tag_t id;
-	char * name;
-};
-
-struct SH_Validator
-{
-	unsigned int tag_n;
-	struct SH_Tag * tags;
-	tag_t last_tag;
-};
-
-struct SH_Validator * SH_Validator_new (struct SH_Error * error);
-void SH_Validator_free (struct SH_Validator * validator,
-			struct SH_Error * error);
-
-tag_t SH_Validator_register_tag (struct SH_Validator * validator,
-				 const char * tag,
-				 struct SH_Error * error);
-
-
-bool SH_Validator_check_tag (struct SH_Validator * validator,
-			     const char * tag);
-
-#endif /* _VALIDATOR_H */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 611f18abaa14ecaa588a25c074f0c29c9b0e8cd0..3053a22bd19ee3328a38ac7e9d429706061654b2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,48 +1,42 @@
 ## Process this file with automake to produce Makefile.in
 
-AM_CFLAGS = -Wall -Wextra $(CHECK_CFLAGS)
+AM_CFLAGS = -Wall -Wextra -Wno-nonnull $(CHECK_CFLAGS)
 
 TESTS =
 TESTS += sefht_cms_test
 TESTS += sefht_data_test
 TESTS += sefht_node_fragment_test
 TESTS += sefht_text_test
-TESTS += sefht_validator_test
 
 check_PROGRAMS = $(TESTS)
 
 AM_CPPFLAGS =
-AM_CPPFLAGS += -I$(top_srcdir)/src
+AM_CPPFLAGS += -DSEFHT_COMPILATION
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/sefht
 
 LDADD = $(CHECK_LIBS)
 
+OBJECT_PREFIX = $(top_builddir)/src/lib/sefht/libsefht_la-
+
 sefht_cms_test_SOURCES = test_cms.c
 sefht_cms_test_LDADD =
-sefht_cms_test_LDADD += $(top_builddir)/src/cms.o
-sefht_cms_test_LDADD += $(top_builddir)/src/data.o
-sefht_cms_test_LDADD += $(top_builddir)/src/validator.o
+sefht_cms_test_LDADD += $(OBJECT_PREFIX)data.o
+sefht_cms_test_LDADD += $(OBJECT_PREFIX)validator.o
 sefht_cms_test_LDADD += $(LDADD)
 
 sefht_data_test_SOURCES = test_data.c
 sefht_data_test_LDADD =
-sefht_data_test_LDADD += $(top_builddir)/src/data.o
-sefht_data_test_LDADD += $(top_builddir)/src/validator.o
+sefht_data_test_LDADD += $(OBJECT_PREFIX)validator.o
 sefht_data_test_LDADD += $(LDADD)
 
 sefht_node_fragment_test_SOURCES = test_node_fragment.c
 sefht_node_fragment_test_LDADD =
-sefht_node_fragment_test_LDADD += $(top_builddir)/src/node_fragment.o
-sefht_node_fragment_test_LDADD += $(top_builddir)/src/data.o
-sefht_node_fragment_test_LDADD += $(top_builddir)/src/fragment.o
-sefht_node_fragment_test_LDADD += $(top_builddir)/src/text.o
-sefht_node_fragment_test_LDADD += $(top_builddir)/src/validator.o
+sefht_node_fragment_test_LDADD += $(OBJECT_PREFIX)data.o
+sefht_node_fragment_test_LDADD += $(OBJECT_PREFIX)fragment.o
+sefht_node_fragment_test_LDADD += $(OBJECT_PREFIX)text.o
+sefht_node_fragment_test_LDADD += $(OBJECT_PREFIX)validator.o
 sefht_node_fragment_test_LDADD += $(LDADD)
 
 sefht_text_test_SOURCES = test_text.c
 sefht_text_test_LDADD =
-sefht_text_test_LDADD += $(top_builddir)/src/text.o
 sefht_text_test_LDADD += $(LDADD)
-
-sefht_validator_test_SOURCES = test_validator.c
-sefht_validator_test_LDADD =
-sefht_validator_test_LDADD += $(LDADD)
diff --git a/tests/test_cms.c b/tests/test_cms.c
index a6cc36fceb549bfe9f7bc03452f7d10cf64f3c14..f35935b9b93fd62dcb97988e0a083357a20d01c5 100644
--- a/tests/test_cms.c
+++ b/tests/test_cms.c
@@ -25,28 +25,27 @@
 #include <check.h>
 #include <stdlib.h>
 
-#include "error.h"
-#include "cms.h"
+#include "status.h"
+
+#include "cms.c"
 
 
 START_TEST(test_cms)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Cms * cms;
 
 	cms = SH_Cms_new (NULL);
 	ck_assert_int_ne ((long int) cms, (long int) NULL);
 
-	SH_Cms_free (cms, NULL);
+	SH_Cms_free (cms);
 
-	error.type = UNDEFINED;
-	cms = SH_Cms_new (&error);
+	_status_preinit (status);
+	cms = SH_Cms_new (&status);
 	ck_assert_int_ne ((long int) cms, (long int) NULL);
-	ck_assert_int_eq (error.type, SUCCESS);
+	ck_assert_int_eq (status.status, SUCCESS);
 
-	error.type = UNDEFINED;
-	SH_Cms_free (cms, &error);
-	ck_assert_int_eq (error.type, SUCCESS);
+	SH_Cms_free (cms);
 }
 END_TEST
 
@@ -66,7 +65,7 @@ Suite * cms_suite (void)
 	return s;
 }
 
-int main (int argc, char **argv)
+int main (void)
 {
 	int number_failed;
 	Suite *s;
diff --git a/tests/test_data.c b/tests/test_data.c
index 2fcd0bb072941e245f975bc17e95e53725a0f43f..9687a5ddf6a7d5bd21ab09ffece0859d76de04f2 100644
--- a/tests/test_data.c
+++ b/tests/test_data.c
@@ -25,13 +25,14 @@
 #include <check.h>
 #include <stdlib.h>
 
-#include "error.h"
-#include "data.h"
+#include "status.h"
+
+#include "data.c"
 
 
 START_TEST(test_data)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Data * data;
 
 	data = SH_Data_new (NULL);
@@ -40,25 +41,23 @@ START_TEST(test_data)
 	ck_assert_int_eq (data->page_n, 0);
 	ck_assert_int_eq (data->last_page, PAGE_ERR);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 
-	error.type = UNDEFINED;
-	data = SH_Data_new (&error);
+	_status_preinit (status);
+	data = SH_Data_new (&status);
 	ck_assert_int_ne ((long int) data, (long int) NULL);
-	ck_assert_int_eq (error.type, SUCCESS);
+	ck_assert_int_eq (status.status, SUCCESS);
 
 	ck_assert_int_eq (data->page_n, 0);
 	ck_assert_int_eq (data->last_page, PAGE_ERR);
 
-	error.type = UNDEFINED;
-	SH_Data_free (data, &error);
-	ck_assert_int_eq (error.type, SUCCESS);
+	SH_Data_free (data);
 }
 END_TEST
 
 START_TEST(test_data_register_page)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Data * data;
 	const char * page1 = "Homepage";
 	const char * page2 = "Login";
@@ -81,12 +80,12 @@ START_TEST(test_data_register_page)
 	ck_assert_str_eq (data->pages[0].name, page1);
 
 	/* fail without error */
-	data->page_n = UINT_MAX;
+	data->page_n = SIZE_MAX;
 
 	page = SH_Data_register_page (data, page2, NULL);
 	ck_assert_int_eq (page, PAGE_ERR);
 
-	ck_assert_int_eq (data->page_n, UINT_MAX);
+	ck_assert_int_eq (data->page_n, SIZE_MAX);
 	ck_assert_int_eq (data->last_page, 1);
 
 	/* fail2 without error */
@@ -102,10 +101,10 @@ START_TEST(test_data_register_page)
 	/* success with error */
 	data->last_page = 1;
 
-	error.type = UNDEFINED;
-	page = SH_Data_register_page (data, page4, &error);
+	_status_preinit (status);
+	page = SH_Data_register_page (data, page4, &status);
 	ck_assert_int_eq (page, 2);
-	ck_assert_int_eq (error.type, SUCCESS);
+	ck_assert_int_eq (status.status, SUCCESS);
 
 	ck_assert_int_eq (data->page_n, 2);
 	ck_assert_int_eq (data->last_page, page);
@@ -114,29 +113,29 @@ START_TEST(test_data_register_page)
 	ck_assert_str_eq (data->pages[1].name, page4);
 
 	/* fail with error */
-	data->page_n = UINT_MAX;
+	data->page_n = SIZE_MAX;
 
-	error.type = UNDEFINED;
-	page = SH_Data_register_page (data, page5, &error);
+	_status_preinit (status);
+	page = SH_Data_register_page (data, page5, &status);
 	ck_assert_int_eq (page, PAGE_ERR);
-	ck_assert_int_eq (error.type, DOMAIN_ERROR);
+	ck_assert_int_eq (status.status, E_DOMAIN);
 
-	ck_assert_int_eq (data->page_n, UINT_MAX);
+	ck_assert_int_eq (data->page_n, SIZE_MAX);
 	ck_assert_int_eq (data->last_page, 2);
 
 	/* fail2 with error */
 	data->page_n = 2;
 	data->last_page = PAGE_MAX;
 
-	error.type = UNDEFINED;
-	page = SH_Data_register_page (data, page6, &error);
+	_status_preinit (status);
+	page = SH_Data_register_page (data, page6, &status);
 	ck_assert_int_eq (page, PAGE_ERR);
-	ck_assert_int_eq (error.type, DOMAIN_ERROR);
+	ck_assert_int_eq (status.status, E_DOMAIN);
 
 	ck_assert_int_eq (data->page_n, 2);
 	ck_assert_int_eq (data->last_page, PAGE_MAX);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 }
 END_TEST
 
@@ -157,7 +156,7 @@ Suite * data_suite (void)
 	return s;
 }
 
-int main (int argc, char **argv)
+int main (void)
 {
 	int number_failed;
 	Suite *s;
diff --git a/tests/test_node_fragment.c b/tests/test_node_fragment.c
index 6ee394bfc85ce679f5af6203b852d01759269bf0..d7c14a1ec6d6d2bea92a06ea23ddeda1da0f18de 100644
--- a/tests/test_node_fragment.c
+++ b/tests/test_node_fragment.c
@@ -27,20 +27,19 @@
 #include <stdlib.h>
 
 #include "macro.h"
-#include "error.h"
-#include "node_fragment.h"
+#include "status.h"
+
+#include "node_fragment.c"
 
 
 START_TEST(test_node_fragment)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Fragment * fragment;
 	struct SH_Data * data;
 	const char * tag = "tag";
-	const char * no_tag = "no_tag";
 
 	data = SH_Data_new (NULL);
-	SH_Validator_register_tag (data->validator, tag, NULL);
 
 	/* valid tag - no error */
 	fragment = SH_NodeFragment_new (tag, data, NULL);
@@ -48,37 +47,25 @@ START_TEST(test_node_fragment)
 
 	ck_assert_str_eq (((struct SH_NodeFragment *) fragment)->tag, tag);
 
-	SH_Fragment_free (fragment, NULL);
-
-	/* invalid tag - no error */
-	fragment = SH_NodeFragment_new (no_tag, data, NULL);
-	ck_assert_int_eq ((long int) fragment, (long int) NULL);
+	SH_Fragment_free (fragment);
 
 	/* valid tag - error */
-	error.type = UNDEFINED;
-	fragment = SH_NodeFragment_new (tag, data, &error);
+	_status_preinit (status);
+	fragment = SH_NodeFragment_new (tag, data, &status);
 	ck_assert_int_ne ((long int) fragment, (long int) NULL);
-	ck_assert_int_eq (error.type, SUCCESS);
+	ck_assert_int_eq (status.status, SUCCESS);
 
 	ck_assert_str_eq (((struct SH_NodeFragment *) fragment)->tag, tag);
 
-	error.type = UNDEFINED;
-	SH_Fragment_free (fragment, &error);
-	ck_assert_int_eq (error.type, SUCCESS);
-
-	/* invalid tag - error */
-	error.type = UNDEFINED;
-	fragment = SH_NodeFragment_new (no_tag, data, &error);
-	ck_assert_int_eq ((long int) fragment, (long int) NULL);
-	ck_assert_int_eq (error.type, VALUE_ERROR);
+	SH_Fragment_free (fragment);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 }
 END_TEST
 
 START_TEST(test_node_fragment_copy)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Fragment * fragment;
 	struct SH_Fragment * copy;
 	struct SH_Data * data;
@@ -99,13 +86,13 @@ START_TEST(test_node_fragment_copy)
 	ck_assert_ptr_ne (((struct SH_NodeFragment *) fragment)->childs,
 			  ((struct SH_NodeFragment *) copy)->childs);
 
-	SH_Fragment_free (copy, NULL);
+	SH_Fragment_free (copy);
 
 	/* with error */
-	error.type = UNDEFINED;
+	_status_preinit (status);
 	copy = SH_NodeFragment_copy (((struct SH_NodeFragment *) fragment),
-				     &error);
-	ck_assert_int_eq (error.type, SUCCESS);
+				     &status);
+	ck_assert_int_eq (status.status, SUCCESS);
 
 	ck_assert_ptr_ne (copy, NULL);
 	ck_assert_ptr_ne (fragment, copy);
@@ -115,10 +102,10 @@ START_TEST(test_node_fragment_copy)
 	ck_assert_ptr_ne (((struct SH_NodeFragment *) fragment)->childs,
 			  ((struct SH_NodeFragment *) copy)->childs);
 
-	SH_Fragment_free (fragment, NULL);
-	SH_Fragment_free (copy, NULL);
+	SH_Fragment_free (fragment);
+	SH_Fragment_free (copy);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 }
 END_TEST
 
@@ -145,7 +132,7 @@ check_childs (struct SH_NodeFragment * fragment, struct SH_NodeFragment * copy)
 
 START_TEST(test_node_fragment_deepcopy)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Fragment * fragment;
 	struct SH_Fragment * child1;
 	struct SH_Fragment * child2;
@@ -197,12 +184,12 @@ START_TEST(test_node_fragment_deepcopy)
 	check_childs (((struct SH_NodeFragment *) fragment),
 		      ((struct SH_NodeFragment *) copy));
 
-	SH_Fragment_free (copy, NULL);
+	SH_Fragment_free (copy);
 
 	/* with error */
-	error.type = UNDEFINED;
-	copy = SH_Fragment_copy (fragment, &error);
-	ck_assert_int_eq (error.type, SUCCESS);
+	_status_preinit (status);
+	copy = SH_Fragment_copy (fragment, &status);
+	ck_assert_int_eq (status.status, SUCCESS);
 
 	ck_assert_ptr_ne (copy, NULL);
 	ck_assert_ptr_ne (fragment, copy);
@@ -216,16 +203,16 @@ START_TEST(test_node_fragment_deepcopy)
 	check_childs (((struct SH_NodeFragment *) fragment),
 		      ((struct SH_NodeFragment *) copy));
 
-	SH_Fragment_free (fragment, NULL);
-	SH_Fragment_free (copy, NULL);
+	SH_Fragment_free (fragment);
+	SH_Fragment_free (copy);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 }
 END_TEST
 
 START_TEST(test_node_fragment_tag)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Fragment * fragment;
 	struct SH_Data * data;
 	char * tag;
@@ -233,8 +220,6 @@ START_TEST(test_node_fragment_tag)
 	const char * tag2 = "body";
 
 	data = SH_Data_new (NULL);
-	SH_Validator_register_tag (data->validator, tag1, NULL);
-	SH_Validator_register_tag (data->validator, tag2, NULL);
 
 	/* no error */
 	fragment = SH_NodeFragment_new (tag1, data, NULL);
@@ -244,26 +229,26 @@ START_TEST(test_node_fragment_tag)
 	ck_assert_str_eq (tag, tag1);
 	free (tag);
 
-	SH_Fragment_free (fragment, NULL);
+	SH_Fragment_free (fragment);
 
 	/* error */
 	fragment = SH_NodeFragment_new (tag2, data, NULL);
 
-	error.type = UNDEFINED;
+	_status_preinit (status);
 	tag = SH_NodeFragment_get_tag (((struct SH_NodeFragment *) fragment),
-				       &error);
+				       &status);
 	ck_assert_str_eq (tag, tag2);
-	ck_assert_int_eq (error.type, SUCCESS);
+	ck_assert_int_eq (status.status, SUCCESS);
 	free (tag);
 
-	SH_Fragment_free (fragment, NULL);
-	SH_Data_free (data, NULL);
+	SH_Fragment_free (fragment);
+	SH_Data_free (data);
 }
 END_TEST
 
 START_TEST(test_node_fragment_child)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Fragment * parent;
 	struct SH_Fragment * child1;
 	struct SH_Fragment * child2;
@@ -290,25 +275,25 @@ START_TEST(test_node_fragment_child)
 	/* with error */
 	ck_assert_int_eq (((struct SH_NodeFragment *) parent)->child_n, 1);
 
-	error.type = UNDEFINED;
+	_status_preinit (status);
 	boolean = SH_NodeFragment_append_child (((struct SH_NodeFragment *) parent),
-						child2, &error);
+						child2, &status);
 	ck_assert_int_eq (boolean, TRUE);
-	ck_assert_int_eq (error.type, SUCCESS);
+	ck_assert_int_eq (status.status, SUCCESS);
 	ck_assert_int_eq (((struct SH_NodeFragment *) parent)->child_n,
 			  2);
 	ck_assert_ptr_eq (((struct SH_NodeFragment *) parent)->childs[1],
 			  child2);
 
-	SH_Fragment_free (parent, NULL);
+	SH_Fragment_free (parent);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 }
 END_TEST
 
 START_TEST(test_node_fragment_get_child)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Fragment * parent;
 	struct SH_Fragment * child1;
 	struct SH_Fragment * child2;
@@ -332,21 +317,21 @@ START_TEST(test_node_fragment_get_child)
 	ck_assert_ptr_eq (NULL, child2);
 
 	/* with error */
-	error.type = UNDEFINED;
+	_status_preinit (status);
 	child2 = SH_NodeFragment_get_child (((struct SH_NodeFragment *) parent),
-					    0, &error);
+					    0, &status);
 	ck_assert_ptr_eq (child1, child2);
-	ck_assert_int_eq (error.type, SUCCESS);
+	ck_assert_int_eq (status.status, SUCCESS);
 
-	error.type = UNDEFINED;
+	_status_preinit (status);
 	child2 = SH_NodeFragment_get_child (((struct SH_NodeFragment *) parent),
-					    1, &error);
+					    1, &status);
 	ck_assert_ptr_eq (NULL, child2);
-	ck_assert_int_eq (error.type, VALUE_ERROR);
+	ck_assert_int_eq (status.status, E_VALUE);
 
-	SH_Fragment_free (parent, NULL);
+	SH_Fragment_free (parent);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 }
 END_TEST
 
@@ -381,9 +366,9 @@ START_TEST(test_node_fragment_is_child)
 					    child2);
 	ck_assert_int_eq (boolean, TRUE);
 
-	SH_Fragment_free (parent, NULL);
+	SH_Fragment_free (parent);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 }
 END_TEST
 
@@ -434,22 +419,23 @@ START_TEST(test_node_fragment_is_descendant)
 						  child3);
 	ck_assert_int_eq (boolean, FALSE);
 
-	SH_Fragment_free (parent, NULL);
+	SH_Fragment_free (parent);
 
-	SH_Data_free (data, NULL);
+	SH_Data_free (data);
 }
 END_TEST
 
 START_TEST(test_node_fragment_html)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Fragment * fragment1;
 	struct SH_Fragment * fragment2;
 	struct SH_Data * data;
 	struct SH_Text * text;
+	char * string;
+	size_t length;
 
 	data = SH_Data_new (NULL);
-	SH_Validator_register_tag (data->validator, "html", NULL);
 
 	/* no error */
 	fragment1 = SH_NodeFragment_new ("html", data, NULL);
@@ -459,14 +445,18 @@ START_TEST(test_node_fragment_html)
 
 	text = SH_Fragment_to_html (fragment1, INLINE, 0, 1, INDENT_TEXT,
 				    NULL);
-	ck_assert_str_eq (text->text, "<html><body></body></html>");
+	string = SH_Text_get_string (text, 0, SIZE_MAX, &length, NULL);
+	ck_assert_str_eq (string, "<html><body></body></html>");
+	free (string);
 
 	text = SH_Fragment_to_html (fragment1, WRAP, 0, 1, INDENT_TEXT,
 				    NULL);
-	ck_assert_str_eq (text->text,
+	string = SH_Text_get_string (text, 0, SIZE_MAX, &length, NULL);
+	ck_assert_str_eq (string,
 			  "<html>\n\t<body>\n\t</body>\n</html>\n");
+	free (string);
 
-	SH_Fragment_free (fragment1, NULL);
+	SH_Fragment_free (fragment1);
 
 	/* error */
 	fragment1 = SH_NodeFragment_new ("html", data, NULL);
@@ -474,21 +464,25 @@ START_TEST(test_node_fragment_html)
 	SH_NodeFragment_append_child (((struct SH_NodeFragment *) fragment1),
 				      fragment2, NULL);
 
-	error.type = UNDEFINED;
+	_status_preinit (status);
 	text = SH_Fragment_to_html (fragment1, INLINE, 0, 1, INDENT_TEXT,
-				    &error);
-	ck_assert_str_eq (text->text, "<html><body></body></html>");
-	ck_assert_int_eq (error.type, SUCCESS);
+				    &status);
+	string = SH_Text_get_string (text, 0, SIZE_MAX, &length, NULL);
+	ck_assert_str_eq (string, "<html><body></body></html>");
+	free (string);
+	ck_assert_int_eq (status.status, SUCCESS);
 
-	error.type = UNDEFINED;
+	_status_preinit (status);
 	text = SH_Fragment_to_html (fragment1, WRAP, 0, 1, INDENT_TEXT,
-				    &error);
-	ck_assert_str_eq (text->text,
+				    &status);
+	string = SH_Text_get_string (text, 0, SIZE_MAX, &length, NULL);
+	ck_assert_str_eq (string,
 			  "<html>\n\t<body>\n\t</body>\n</html>\n");
-	ck_assert_int_eq (error.type, SUCCESS);
+	free (string);
+	ck_assert_int_eq (status.status, SUCCESS);
 
-	SH_Fragment_free (fragment1, NULL);
-	SH_Data_free (data, NULL);
+	SH_Fragment_free (fragment1);
+	SH_Data_free (data);
 }
 END_TEST
 
@@ -516,7 +510,7 @@ Suite * fragment_suite (void)
 	return s;
 }
 
-int main (int argc, char **argv)
+int main (void)
 {
 	int number_failed;
 	Suite *s;
diff --git a/tests/test_text.c b/tests/test_text.c
index 5158f122f59541aadcd975eb221fe285790e74b9..986ae71cfd80ff8c49e05a7ddaf896a9407b03fd 100644
--- a/tests/test_text.c
+++ b/tests/test_text.c
@@ -27,186 +27,432 @@
 #include <stdbool.h>
 #include <stdlib.h>
 
+/* lower SIZE_MAX as we try to reach it */
+#include <limits.h>
+#undef SIZE_MAX
+#define SIZE_MAX 30
+
 #include "macro.h"
-#include "error.h"
+#include "status.h"
 
-#include "text.h"
+/* C file is needed, because we want to override CHUNK_SIZE */
+#define CHUNK_SIZE 5
+#include "text.c"
 
 
 START_TEST(test_text)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Text * text;
 
 	text = SH_Text_new (NULL);
 	ck_assert_int_ne ((long int) text, (long int) NULL);
 
-	ck_assert_int_ge  (text->size, CHUNK_SIZE);
+	ck_assert_int_ge  (text->data->size, CHUNK_SIZE);
 
-	SH_Text_free (text, NULL);
+	SH_Text_free (text);
 
-	error.type = UNDEFINED;
-	text = SH_Text_new (&error);
+	_status_preinit (status);
+	text = SH_Text_new (&status);
 	ck_assert_int_ne ((long int) text, (long int) NULL);
-	ck_assert_int_eq (error.type, SUCCESS);
+	ck_assert_int_eq (status.status, SUCCESS);
 
-	ck_assert_int_ge  (text->size, CHUNK_SIZE);
+	ck_assert_int_ge  (text->data->size, CHUNK_SIZE);
 
-	error.type = UNDEFINED;
-	SH_Text_free (text, &error);
-	ck_assert_int_eq (error.type, SUCCESS);
+	SH_Text_free (text);
 }
 END_TEST
 
-START_TEST(test_text_enlarge)
+START_TEST(test_text_string)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Text * text;
-	bool result;
-	size_t size1;
-	size_t size2;
-	size_t size2_;
-	size_t size3;
 
+	/* without error */
+	text = SH_Text_new_from_string ("12345", NULL);
+	ck_assert_int_ne ((long int) text, (long int) NULL);
 
-	size1 = CHUNK_SIZE;
-	size2 = CHUNK_SIZE + 1;
-	size2_ = 2 * CHUNK_SIZE;
-	if (SIZE_MAX % CHUNK_SIZE == 0)
-	{
-		size3 = ((SIZE_MAX / CHUNK_SIZE) - 1) * CHUNK_SIZE;
-	}
-	else
+	ck_assert_int_eq (text->data->length, 5);
+	ck_assert_int_eq (text->data->size, 6);
+	ck_assert_str_eq (text->data->text, "12345");
+	ck_assert_ptr_eq (text->data->next, NULL);
+
+	SH_Text_free (text);
+
+
+	/* with error */
+	_status_preinit (status);
+	text = SH_Text_new_from_string ("12345", &status);
+	ck_assert_int_ne ((long int) text, (long int) NULL);
+	ck_assert_int_eq (status.status, SUCCESS);
+
+	ck_assert_int_eq (text->data->length, 5);
+	ck_assert_int_eq (text->data->size, 6);
+	ck_assert_str_eq (text->data->text, "12345");
+	ck_assert_ptr_eq (text->data->next, NULL);
+
+	SH_Text_free (text);
+
+}
+END_TEST
+
+START_TEST (test_text_copy)
+{
+	struct SH_Status status;
+	struct SH_Text * text;
+	struct SH_Text * copy;
+	char * text_string;
+	char * copy_string;
+
+	text = SH_Text_new_from_string ("Text12345", NULL);
+	text_string = SH_Text_get_string (text, 0, 10, NULL, NULL);
+
+	/* without error */
+	copy = SH_Text_copy (text, NULL);
+	ck_assert_ptr_ne (copy, NULL);
+	ck_assert_ptr_ne (copy, text);
+	copy_string = SH_Text_get_string (copy, 0, 10, NULL, NULL);
+	ck_assert_str_eq (copy_string, text_string);
+
+	free (copy_string);
+	SH_Text_free (copy);
+
+	/* with error */
+	_status_preinit (status);
+	copy = SH_Text_copy (text, &status);
+	ck_assert_int_eq (status.status, SUCCESS);
+	ck_assert_ptr_ne (copy, NULL);
+	ck_assert_ptr_ne (copy, text);
+	copy_string = SH_Text_get_string (copy, 0, 10, NULL, NULL);
+	ck_assert_str_eq (copy_string, text_string);
+
+	free (copy_string);
+	SH_Text_free (copy);
+
+	free (text_string);
+	SH_Text_free (text);
+}
+END_TEST
+
+START_TEST(test_text_get_length)
+{
+	struct SH_Status status;
+	struct SH_Text * text;
+	size_t length;
+	char index[2] = {1, 0};
+
+	text = SH_Text_new (NULL);
+
+	/* length == 0 - without error */
+	length = SH_Text_get_length (text, NULL);
+	ck_assert_int_eq (length, 0);
+
+	/* length == 0 - with error */
+	_status_preinit (status);
+	length = SH_Text_get_length (text, &status);
+	ck_assert_int_eq (length, 0);
+	ck_assert_int_eq (status.status, SUCCESS);
+
+
+	for (; index[0] <= SIZE_MAX; index[0]++)
 	{
-		size3 = (SIZE_MAX / CHUNK_SIZE) * CHUNK_SIZE;
+		SH_Text_append_string (text, index, NULL);
 	}
 
-	text = SH_Text_new (NULL);
+	/* length == SIZE_MAX - without error */
+	length = SH_Text_get_length (text, NULL);
+	ck_assert_int_eq (length, SIZE_MAX);
 
-	/* size == current size */
-	result = SH_Text_enlarge (text, size1, NULL);
-	ck_assert_int_eq (result, TRUE);
+	/* length == SIZE_MAX - with error */
+	_status_preinit (status);
+	length = SH_Text_get_length (text, &status);
+	ck_assert_int_eq (length, SIZE_MAX);
+	ck_assert_int_eq (status.status, SUCCESS);
 
-	ck_assert_int_eq  (text->size, size1);
+	SH_Text_append_string (text, index, NULL);
 
-	/* size > current size */
-	result = SH_Text_enlarge (text, size2, NULL);
-	ck_assert_int_eq (result, TRUE);
+	/* length > SIZE_MAX - without error */
+	length = SH_Text_get_length (text, NULL);
+	ck_assert_int_eq (length, SIZE_MAX);
 
-	ck_assert_int_eq (text->size, size2_);
+	/* length > SIZE_MAX - with error */
+	_status_preinit (status);
+	length = SH_Text_get_length (text, &status);
+	ck_assert_int_eq (length, SIZE_MAX);
+	ck_assert_int_eq (status.status, E_DOMAIN);
 
-	/* size < current size */
-	result = SH_Text_enlarge (text, size1, NULL);
-	ck_assert_int_eq (result, TRUE);
+	SH_Text_free (text);
+}
+END_TEST
 
-	ck_assert_int_eq (text->size, size2_);
+START_TEST(test_text_get_char)
+{
+	struct SH_Status status;
+	struct SH_Text * text;
+	unsigned int i;
+	char * c;
 
-	/* size throw overflow */
-	result = SH_Text_enlarge (text, size3, NULL);
-	ck_assert_int_eq (result, FALSE);
+	text = SH_Text_new_from_string ("abcd", NULL);
+	SH_Text_append_string (text, "efgh", NULL);
+	SH_Text_append_string (text, "ijkl", NULL);
 
-	SH_Text_free (text, NULL);
+	/* success - without error */
+	for (i = 0; i < 12; i++)
+	{
+		c = SH_Text_get_char (text, i, NULL);
+		ck_assert_ptr_ne (c, NULL);
+		ck_assert_int_eq (*c, 97 + i);
+		free (c);
+	}
 
+	/* wrong index - without error */
+	c = SH_Text_get_char (text, 13, NULL);
+	ck_assert_ptr_eq (c, NULL);
 
-	text = SH_Text_new (NULL);
+	/* success - with error */
+	for (i = 0; i < 12; i++)
+	{
+		_status_preinit (status);
+		c = SH_Text_get_char (text, i, &status);
+		ck_assert_ptr_ne (c, NULL);
+		ck_assert_int_eq (*c, 97 + i);
+		ck_assert_int_eq (status.status, SUCCESS);
+		free (c);
+	}
 
-	/* size == current size */
-	error.type = UNDEFINED;
-	result = SH_Text_enlarge (text, size1, &error);
-	ck_assert_int_eq (result, TRUE);
-	ck_assert_int_eq (error.type, SUCCESS);
+	/* wrong index - with error */
+	_status_preinit (status);
+	c = SH_Text_get_char (text, 13, &status);
+	ck_assert_ptr_eq (c, NULL);
+	ck_assert_int_eq (status.status, E_VALUE);
 
-	ck_assert_int_eq  (text->size, size1);
+	SH_Text_free (text);
+}
+END_TEST
 
-	/* size > current size */
-	error.type = UNDEFINED;
-	result = SH_Text_enlarge (text, size2, &error);
-	ck_assert_int_eq (result, TRUE);
-	ck_assert_int_eq (error.type, SUCCESS);
+START_TEST(test_text_get_string)
+{
+	struct SH_Status status;
+	struct SH_Text * text;
+	char * result;
+	size_t length = -1;
+	char index[2] = {1, 0};
 
-	ck_assert_int_eq (text->size, size2_);
+	text = SH_Text_new (NULL);
+	for (; index[0] < 100; index[0]++)
+	{
+		SH_Text_append_string (text, index, NULL);
+	}
 
-	/* size < current size */
-	error.type = UNDEFINED;
-	result = SH_Text_enlarge (text, size1, &error);
-	ck_assert_int_eq (result, TRUE);
-	ck_assert_int_eq (error.type, SUCCESS);
+	/* single segment - without error */
+	result = SH_Text_get_string (text, 59, 8, &length, NULL);
+	ck_assert_str_eq (result, "<=>?@ABC");
+	ck_assert_int_eq (length, 8);
+	free (result);
+
+	/* multiple segment - without error */
+	result = SH_Text_get_string (text, 47, 43, &length, NULL);
+	ck_assert_str_eq (result, "0123456789:;<=>?@"
+	                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+	ck_assert_int_eq (length, 43);
+	free (result);
+
+	/* pass over end - without error */
+	result = SH_Text_get_string (text, 96, 10, &length, NULL);
+	ck_assert_str_eq (result, "abc");
+	ck_assert_int_eq (length, 3);
+	free (result);
+
+	/* out of range - without error */
+	result = SH_Text_get_string (text, 99, 0, &length, NULL);
+	ck_assert_ptr_eq (result, NULL);
+	ck_assert_int_eq (length, 0);
+
+
+	/* single segment - with error */
+	_status_preinit (status);
+	result = SH_Text_get_string (text, 59, 8, &length, &status);
+	ck_assert_str_eq (result, "<=>?@ABC");
+	ck_assert_int_eq (length, 8);
+	ck_assert_int_eq (status.status, SUCCESS);
+	free (result);
+
+	/* multiple segment - with error */
+	_status_preinit (status);
+	result = SH_Text_get_string (text, 47, 43, &length, &status);
+	ck_assert_str_eq (result, "0123456789:;<=>?@"
+	                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+	ck_assert_int_eq (length, 43);
+	ck_assert_int_eq (status.status, SUCCESS);
+	free (result);
+
+	/* pass over end - with error */
+	_status_preinit (status);
+	result = SH_Text_get_string (text, 96, 10, &length, &status);
+	ck_assert_str_eq (result, "abc");
+	ck_assert_int_eq (length, 3);
+	ck_assert_int_eq (status.status, SUCCESS);
+	free (result);
+
+	/* out of range - without error */
+	_status_preinit (status);
+	result = SH_Text_get_string (text, 99, 0, &length, &status);
+	ck_assert_ptr_eq (result, NULL);
+	ck_assert_int_eq (length, 0);
+	ck_assert_int_eq (status.status, E_VALUE);
+
+	SH_Text_free (text);
+}
+END_TEST
 
-	ck_assert_int_eq (text->size, size2_);
+START_TEST(test_text_get_range)
+{
+	struct SH_Status status;
+	struct SH_Text * text;
+	char * result;
+	size_t length = -1;
+	char index[2] = {1, 0};
 
-	/* size throw overflow */
-	error.type = UNDEFINED;
-	result = SH_Text_enlarge (text, size3, &error);
-	ck_assert_int_eq (result, FALSE);
-	ck_assert_int_eq (error.type, DOMAIN_ERROR);
+	text = SH_Text_new (NULL);
+	for (; index[0] < 100; index[0]++)
+	{
+		SH_Text_append_string (text, index, NULL);
+	}
 
-	SH_Text_free (text, NULL);
+	/* single segment - without error */
+	result = SH_Text_get_range (text, 59, 67, &length, NULL);
+	ck_assert_str_eq (result, "<=>?@ABC");
+	ck_assert_int_eq (length, 8);
+	free (result);
+
+	/* multiple segment - without error */
+	result = SH_Text_get_range (text, 47, 90, &length, NULL);
+	ck_assert_str_eq (result, "0123456789:;<=>?@"
+	                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+	ck_assert_int_eq (length, 43);
+	free (result);
+
+	/* pass over end - without error */
+	result = SH_Text_get_range (text, 96, 106, &length, NULL);
+	ck_assert_str_eq (result, "abc");
+	ck_assert_int_eq (length, 3);
+	free (result);
+
+	/* out of range - without error */
+	result = SH_Text_get_range (text, 99, 99, &length, NULL);
+	ck_assert_ptr_eq (result, NULL);
+	ck_assert_int_eq (length, 0);
+
+
+	/* single segment - with error */
+	_status_preinit (status);
+	result = SH_Text_get_range (text, 59, 67, &length, &status);
+	ck_assert_str_eq (result, "<=>?@ABC");
+	ck_assert_int_eq (length, 8);
+	ck_assert_int_eq (status.status, SUCCESS);
+	free (result);
+
+	/* multiple segment - with error */
+	_status_preinit (status);
+	result = SH_Text_get_range (text, 47, 90, &length, &status);
+	ck_assert_str_eq (result, "0123456789:;<=>?@"
+	                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+	ck_assert_int_eq (length, 43);
+	ck_assert_int_eq (status.status, SUCCESS);
+	free (result);
+
+	/* pass over end - with error */
+	_status_preinit (status);
+	result = SH_Text_get_range (text, 96, 106, &length, &status);
+	ck_assert_str_eq (result, "abc");
+	ck_assert_int_eq (length, 3);
+	ck_assert_int_eq (status.status, SUCCESS);
+	free (result);
+
+	/* out of range - without error */
+	_status_preinit (status);
+	result = SH_Text_get_range (text, 99, 99, &length, &status);
+	ck_assert_ptr_eq (result, NULL);
+	ck_assert_int_eq (length, 0);
+	ck_assert_int_eq (status.status, E_VALUE);
+
+	SH_Text_free (text);
 }
 END_TEST
 
 START_TEST (test_text_append)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Text * text1;
 	struct SH_Text * text2;
-	struct SH_Text * text3;
 	bool boolean;
+	char * string;
+	size_t length;
 
 	text1 = SH_Text_new (NULL);
 	text2 = SH_Text_new (NULL);
-	text3 = SH_Text_new (NULL);
 
+	/* append_string - without error */
 	boolean = SH_Text_append_string (text1, "Text1", NULL);
 	ck_assert_int_eq (boolean, TRUE);
-	ck_assert_str_eq (text1->text, "Text1");
-	ck_assert_int_eq (text1->length, 5);
-	ck_assert_int_ge (text1->size, 6);
-
-	boolean = SH_Text_append_string (text2, "Text2", NULL);
-	ck_assert_int_eq (boolean, TRUE);
-	ck_assert_str_eq (text2->text, "Text2");
-	ck_assert_int_eq (text2->length, 5);
-	ck_assert_int_ge (text2->size, 6);
-
-	error.type = UNDEFINED;
-	boolean = SH_Text_append_string (text3, "Text3", &error);
+	string = SH_Text_get_string (text1, 0, SIZE_MAX, &length, NULL);
+	ck_assert_str_eq (string, "Text1");
+	ck_assert_int_eq (length, 5);
+	free (string);
+
+	/* append_string - with error */
+	_status_preinit (status);
+	boolean = SH_Text_append_string (text2, "Text2", &status);
 	ck_assert_int_eq (boolean, TRUE);
-	ck_assert_int_eq (error.type, SUCCESS);
-	ck_assert_str_eq (text3->text, "Text3");
-	ck_assert_int_eq (text3->length, 5);
-	ck_assert_int_ge (text3->size, 6);
+	ck_assert_int_eq (status.status, SUCCESS);
+	string = SH_Text_get_string (text2, 0, SIZE_MAX, &length, NULL);
+	ck_assert_str_eq (string, "Text2");
+	ck_assert_int_eq (length, 5);
+	free (string);
 
 
+	/* append_text - without error */
 	boolean = SH_Text_append_text (text1, text2, NULL);
 	ck_assert_int_eq (boolean, TRUE);
-	ck_assert_str_eq (text1->text, "Text1Text2");
-	ck_assert_int_eq (text1->length, 10);
-	ck_assert_int_ge (text1->size, 11);
-
-	error.type = UNDEFINED;
-	boolean = SH_Text_append_text (text1, text3, &error);
+	string = SH_Text_get_string (text1, 0, SIZE_MAX, &length, NULL);
+	ck_assert_str_eq (string, "Text1Text2");
+	ck_assert_int_eq (length, 10);
+	free (string);
+
+	/* append_text - with error */
+	_status_preinit (status);
+	boolean = SH_Text_append_text (text1, text2, &status);
 	ck_assert_int_eq (boolean, TRUE);
-	ck_assert_int_eq (error.type, SUCCESS);
-	ck_assert_str_eq (text1->text, "Text1Text2Text3");
-	ck_assert_int_eq (text1->length, 15);
-	ck_assert_int_ge (text1->size, 16);
+	ck_assert_int_eq (status.status, SUCCESS);
+	string = SH_Text_get_string (text1, 0, SIZE_MAX, &length, NULL);
+	ck_assert_str_eq (string, "Text1Text2Text2");
+	ck_assert_int_eq (length, 15);
+	free (string);
 
 
-	boolean = SH_Text_join (text1, text2, NULL);
-	ck_assert_int_eq (boolean, TRUE);
-	ck_assert_str_eq (text1->text, "Text1Text2Text3Text2");
-	ck_assert_int_eq (text1->length, 20);
-	ck_assert_int_ge (text1->size, 21);
+	/* join - no error */
+	SH_Text_join (text1, text2);
+	string = SH_Text_get_string (text1, 0, SIZE_MAX, &length, NULL);
+	ck_assert_str_eq (string, "Text1Text2Text2Text2");
+	ck_assert_int_eq (length, 20);
+	free (string);
 
-	error.type = UNDEFINED;
-	boolean = SH_Text_join (text1, text3, &error);
-	ck_assert_int_eq (boolean, TRUE);
-	ck_assert_int_eq (error.type, SUCCESS);
-	ck_assert_str_eq (text1->text, "Text1Text2Text3Text2Text3");
-	ck_assert_int_eq (text1->length, 25);
-	ck_assert_int_ge (text1->size, 26);
 
-	SH_Text_free (text1, NULL);
+	SH_Text_free (text1);
+}
+END_TEST
+
+START_TEST(test_text_print)
+{
+	struct SH_Text * text;
+
+	text = SH_Text_new_from_string ("abcdefghijkklmnopqrstuvwxyz",
+					NULL);
+	SH_Text_append_text (text, text, NULL);
+
+	SH_Text_print (text);
+
+	SH_Text_free (text);
 }
 END_TEST
 
@@ -221,14 +467,20 @@ Suite * text_suite (void)
 	tc_core = tcase_create ("Core");
 
 	tcase_add_test (tc_core, test_text);
-	tcase_add_test (tc_core, test_text_enlarge);
+	tcase_add_test (tc_core, test_text_string);
+	tcase_add_test (tc_core, test_text_copy);
+	tcase_add_test (tc_core, test_text_get_length);
+	tcase_add_test (tc_core, test_text_get_char);
+	tcase_add_test (tc_core, test_text_get_string);
+	tcase_add_test (tc_core, test_text_get_range);
 	tcase_add_test (tc_core, test_text_append);
+	tcase_add_test (tc_core, test_text_print);
 	suite_add_tcase (s, tc_core);
 
 	return s;
 }
 
-int main (int argc, char **argv)
+int main (void)
 {
 	int number_failed;
 	Suite *s;
diff --git a/tests/test_validator.c b/tests/test_validator.c
index bb0f4826eb4d34bc7fbc60f586b81438d65205d8..73072bcc42036250f5040f01d56b466cc64a814a 100644
--- a/tests/test_validator.c
+++ b/tests/test_validator.c
@@ -31,41 +31,39 @@
 /* test static method */
 #define static
 
-/* lower UINT_MAX as we try to reach it */
+/* lower SIZE_MAX as we try to reach it */
 #include <limits.h>
-#define UINT_MAX 20
+#define SIZE_MAX 10 * sizeof (struct tag_info)
 
 #include "macro.h"
-#include "error.h"
+#include "status.h"
 
-/* C file is needed, because we wan't to override UINT_MAX and static */
+/* C file is needed, because we wan't to override SIZE_MAX and static */
 #include "validator.c"
 
 
 START_TEST(test_validator)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Validator * validator;
 
 	validator = SH_Validator_new (NULL);
 	ck_assert_int_ne ((long int) validator, (long int) NULL);
 
-	SH_Validator_free (validator, NULL);
+	SH_Validator_free (validator);
 
-	error.type = UNDEFINED;
-	validator = SH_Validator_new (&error);
+	_status_preinit (status);
+	validator = SH_Validator_new (&status);
 	ck_assert_int_ne ((long int) validator, (long int) NULL);
-	ck_assert_int_eq (error.type, SUCCESS);
+	ck_assert_int_eq (status.status, SUCCESS);
 
-	error.type = UNDEFINED;
-	SH_Validator_free (validator, &error);
-	ck_assert_int_eq (error.type, SUCCESS);
+	SH_Validator_free (validator);
 }
 END_TEST
 
 START_TEST(test_validator_tag)
 {
-	struct SH_Error error;
+	struct SH_Status status;
 	struct SH_Validator * validator;
 	const char * tag1 = "html";
 	const char * tag2 = "head";
@@ -74,8 +72,8 @@ START_TEST(test_validator_tag)
 	const char * tag5 = "main";
 	const char * tag6 = "article";
 	char * tagN;
-	tag_t tag;
-	tag_t tag_return;
+	Tag tag;
+	Tag tag_return;
 	bool check;
 
 	validator = SH_Validator_new (NULL);
@@ -87,10 +85,10 @@ START_TEST(test_validator_tag)
 	ck_assert_int_eq (validator->tag_n, 1);
 	ck_assert_int_eq (validator->last_tag, tag);
 
-	ck_assert_int_eq (validator->tags[0].id, tag);
-	ck_assert_str_eq (validator->tags[0].name, tag1);
+	ck_assert_int_eq (validator->tags[1].data.id, tag);
+	ck_assert_str_eq (validator->tags[1].data.name, tag1);
 
-	tag_return = SH_Validator_get_tag (validator, tag1);
+	tag_return = get_tag_id_by_name (validator, tag1);
 	ck_assert_int_eq (tag_return, tag);
 
 	/* retry without error */
@@ -100,8 +98,8 @@ START_TEST(test_validator_tag)
 	ck_assert_int_eq (validator->tag_n, 1);
 	ck_assert_int_eq (validator->last_tag, tag);
 
-	ck_assert_int_eq (validator->tags[0].id, tag);
-	ck_assert_str_eq (validator->tags[0].name, tag1);
+	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
@@ -112,15 +110,15 @@ START_TEST(test_validator_tag)
 	/* +3 "tag" */
 	/* +1 NULL */
 	/* = +5 */
-	tagN = calloc (((int) floor (log10 ((double) UINT_MAX))) + 5,
+	tagN = calloc (((int) floor (log10 ((double) SIZE_MAX))) + 5,
 		       sizeof (char));
 
 	/* fill with garbage */
-	while (validator->tag_n != UINT_MAX)
+	sprintf (tagN, "tag%lu", validator->tag_n);
+	while (SH_Validator_register_tag (validator, tagN, NULL) != TAG_ERR)
 	{
-		printf ("tag%d\n", validator->tag_n);
-		sprintf (tagN, "tag%d", validator->tag_n);
-		SH_Validator_register_tag (validator, tagN, NULL);
+		printf ("tag%lu\n", validator->tag_n);
+		sprintf (tagN, "tag%lu", validator->tag_n);
 	}
 
 	free (tagN);
@@ -128,10 +126,9 @@ START_TEST(test_validator_tag)
 	tag = SH_Validator_register_tag (validator, tag2, NULL);
 	ck_assert_int_eq (tag, TAG_ERR);
 
-	ck_assert_int_eq (validator->tag_n, UINT_MAX);
-	ck_assert_int_eq (validator->last_tag, (tag_t) UINT_MAX);
+	ck_assert_int_eq (validator->tag_n, 9);
 
-	tag_return = SH_Validator_get_tag (validator, tag2);
+	tag_return = get_tag_id_by_name (validator, tag2);
 	ck_assert_int_eq (tag_return, TAG_ERR);
 
 	/* fail2 without error */
@@ -144,11 +141,11 @@ START_TEST(test_validator_tag)
 	ck_assert_int_eq (validator->tag_n, 1);
 	ck_assert_int_eq (validator->last_tag, TAG_MAX);
 
-	tag_return = SH_Validator_get_tag (validator, tag3);
+	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 = UINT_MAX;
+	validator->tag_n = 9;
 
 	/* check tag */
 	check = SH_Validator_check_tag (validator, tag1);
@@ -160,24 +157,24 @@ START_TEST(test_validator_tag)
 	check = SH_Validator_check_tag (validator, tag3);
 	ck_assert_int_eq (check, FALSE);
 
-	SH_Validator_free (validator, NULL);
+	SH_Validator_free (validator);
 
 
 	validator = SH_Validator_new (NULL);
 
 	/* success with error */
-	error.type = UNDEFINED;
-	tag = SH_Validator_register_tag (validator, tag4, &error);
+	_status_preinit (status);
+	tag = SH_Validator_register_tag (validator, tag4, &status);
 	ck_assert_int_eq (tag, 1);
-	ck_assert_int_eq (error.type, SUCCESS);
+	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[0].id, tag);
-	ck_assert_str_eq (validator->tags[0].name, tag4);
+	ck_assert_int_eq (validator->tags[1].data.id, tag);
+	ck_assert_str_eq (validator->tags[1].data.name, tag4);
 
-	tag_return = SH_Validator_get_tag (validator, tag4);
+	tag_return = get_tag_id_by_name (validator, tag4);
 	ck_assert_int_eq (tag_return, tag);
 
 	/* fail with error */
@@ -189,43 +186,42 @@ START_TEST(test_validator_tag)
 	/* +3 "tag" */
 	/* +1 NULL */
 	/* = +5 */
-	tagN = calloc (((int) floor (log10 ((double) UINT_MAX))) + 5,
+	tagN = calloc (((int) floor (log10 ((double) SIZE_MAX))) + 5,
 		       sizeof (char));
 
 	/* fill with garbage */
-	while (validator->tag_n != UINT_MAX)
+		sprintf (tagN, "tag%lu", validator->tag_n);
+	while (SH_Validator_register_tag (validator, tagN, NULL) != TAG_ERR)
 	{
-		printf ("tag%d\n", validator->tag_n);
-		sprintf (tagN, "tag%d", validator->tag_n);
-		SH_Validator_register_tag (validator, tagN, NULL);
+		printf ("tag%lu\n", validator->tag_n);
+		sprintf (tagN, "tag%lu", validator->tag_n);
 	}
 
 	free (tagN);
 
-	error.type = UNDEFINED;
-	tag = SH_Validator_register_tag (validator, tag5, &error);
+	_status_preinit (status);
+	tag = SH_Validator_register_tag (validator, tag5, &status);
 	ck_assert_int_eq (tag, TAG_ERR);
-	ck_assert_int_eq (error.type, DOMAIN_ERROR);
+	ck_assert_int_eq (status.status, E_DOMAIN);
 
-	ck_assert_int_eq (validator->tag_n, UINT_MAX);
-	ck_assert_int_eq (validator->last_tag, (tag_t) UINT_MAX);
+	ck_assert_int_eq (validator->tag_n, 9);
 
-	tag_return = SH_Validator_get_tag (validator, tag5);
+	tag_return = get_tag_id_by_name (validator, tag5);
 	ck_assert_int_eq (tag_return, TAG_ERR);
 
 	/* fail2 with error */
 	validator->tag_n = 1;
 	validator->last_tag = TAG_MAX;
 
-	error.type = UNDEFINED;
-	tag = SH_Validator_register_tag (validator, tag6, &error);
+	_status_preinit (status);
+	tag = SH_Validator_register_tag (validator, tag6, &status);
 	ck_assert_int_eq (tag, TAG_ERR);
-	ck_assert_int_eq (error.type, DOMAIN_ERROR);
+	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 = SH_Validator_get_tag (validator, tag6);
+	tag_return = get_tag_id_by_name (validator, tag6);
 	ck_assert_int_eq (tag_return, TAG_ERR);
 
 
@@ -240,9 +236,9 @@ START_TEST(test_validator_tag)
 	ck_assert_int_eq (check, FALSE);
 
 	/* also free garbage created for overflow test */
-	validator->tag_n = UINT_MAX;
+	validator->tag_n = 9;
 
-	SH_Validator_free (validator, NULL);
+	SH_Validator_free (validator);
 }
 END_TEST
 
@@ -263,7 +259,7 @@ Suite * validator_suite (void)
 	return s;
 }
 
-int main (int argc, char **argv)
+int main (void)
 {
 	int number_failed;
 	Suite *s;
diff --git a/todo.txt b/todo.txt
index 4a979f61ab8d09de9510c027b892f046c40dd211..e99c86c32a2cfc571af67384635d3dc41df0cd13 100644
--- a/todo.txt
+++ b/todo.txt
@@ -3,3 +3,9 @@ create Logger
 create Docs
 
 dynamic Validator initialization
+
+remove -Wno-nonnull from AM_CFLAGS
+fix warnings for tests
+
+rewrite validator test
+restructure validator