user types CHANGE a new installed user type example

It does only validation, so user can work with
all the values just like before.
diff --git a/src/libyang.h.in b/src/libyang.h.in
index 126e769..2fa32fa 100644
--- a/src/libyang.h.in
+++ b/src/libyang.h.in
@@ -481,9 +481,13 @@
  * the callback is allowed to store anything in the union. Another example, if there are many strings being created and
  * handled, is to store the string length instead having 2 pointers to the same string.
  *
+ * Also, it is possible to perform some additional validation of the value except for the standard YANG one. Even if
+ * the value should only be validated, this callback can be defined and used, it will just keep the #lyd_val value
+ * unchanged.
+ *
  * @subsection typeplugins User Type Plugins
  *
- * There is a simple example user type plugin in `src/user_types` that is not compiled nor installed.
+ * There are simple example user type plugins in `src/user_types`.
  *
  * - ::lytype_plugin_list - plugin is supposed to provide callbacks for:
  *   + @link lytype_store_clb storing the value itself @endlink
diff --git a/src/user_types/CMakeLists.txt b/src/user_types/CMakeLists.txt
new file mode 100644
index 0000000..c15d1e9
--- /dev/null
+++ b/src/user_types/CMakeLists.txt
@@ -0,0 +1,8 @@
+macro(USER_TYPE_PLUGIN PLUGIN_NAME SRCS)
+    add_library(${PLUGIN_NAME} SHARED ${SRCS})
+    set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "")
+    target_link_libraries(${PLUGIN_NAME} yang)
+    install(TARGETS ${PLUGIN_NAME} DESTINATION ${USER_TYPES_PLUGINS_DIR_MACRO})
+endmacro(USER_TYPE_PLUGIN)
+
+USER_TYPE_PLUGIN(user_date_and_time "user_date_and_time.c")
diff --git a/src/user_types/user_date_and_time.c b/src/user_types/user_date_and_time.c
new file mode 100644
index 0000000..04ac129
--- /dev/null
+++ b/src/user_types/user_date_and_time.c
@@ -0,0 +1,258 @@
+/**
+ * @file user_date_and_time.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Example implementation of an date-and-time as a user type, only validation is performed
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _XOPEN_SOURCE
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <ctype.h>
+
+#include <libyang/user_types.h>
+
+#ifdef __GNUC__
+#  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+#else
+#  define UNUSED(x) UNUSED_ ## x
+#endif
+
+static const char *gmt_offsets[] = {
+    "+00:00",
+    "+00:20",
+    "+00:30",
+    "+01:00",
+    "+01:24",
+    "+01:30",
+    "+02:00",
+    "+02:30",
+    "+03:00",
+    "+03:30",
+    "+04:00",
+    "+04:30",
+    "+04:51",
+    "+05:00",
+    "+05:30",
+    "+05:40",
+    "+05:45",
+    "+06:00",
+    "+06:30",
+    "+07:00",
+    "+07:20",
+    "+07:30",
+    "+08:00",
+    "+08:30",
+    "+08:45",
+    "+09:00",
+    "+09:30",
+    "+09:45",
+    "+10:00",
+    "+10:30",
+    "+11:00",
+    "+11:30",
+    "+12:00",
+    "+12:45",
+    "+13:00",
+    "+13:45",
+    "+14:00",
+    "-00:00",
+    "-00:44",
+    "-01:00",
+    "-02:00",
+    "-02:30",
+    "-03:00",
+    "-03:30",
+    "-04:00",
+    "-04:30",
+    "-05:00",
+    "-06:00",
+    "-07:00",
+    "-08:00",
+    "-08:30",
+    "-09:00",
+    "-09:30",
+    "-10:00",
+    "-10:30",
+    "-11:00",
+    "-12:00",
+};
+
+static int
+date_and_time_store_clb(const char *UNUSED(type_name), const char *value_str, lyd_val *UNUSED(value), char **err_msg)
+{
+    struct tm tm = {0}, tm2;
+    uint32_t i, j, k;
+
+    /* \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})
+     * 2018-03-21T09:11:05(.5)(Z|+02:00) */
+    i = 0;
+
+    /* year */
+    tm.tm_year = atoi(value_str + i);
+    /* if there was some invalid number, it will either be discovered in the loop below or by mktime() */
+    tm.tm_year -= 1900;
+    for (j = i + 4; i < j; ++i) {
+        if (!isdigit(value_str[i])) {
+            asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", value_str[i], i, value_str);
+            return 1;
+        }
+    }
+    if (value_str[i] != '-') {
+        asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", '-' expected.", value_str[i], i, value_str);
+        return 1;
+    }
+    ++i;
+
+    /* month */
+    tm.tm_mon = atoi(value_str + i);
+    for (j = i + 2; i < j; ++i) {
+        if (!isdigit(value_str[i])) {
+            asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", value_str[i], i, value_str);
+            return 1;
+        }
+    }
+    if (value_str[i] != '-') {
+        asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", '-' expected.", value_str[i], i, value_str);
+        return 1;
+    }
+    ++i;
+
+    /* day */
+    tm.tm_mday = atoi(value_str + i);
+    for (j = i + 2; i < j; ++i) {
+        if (!isdigit(value_str[i])) {
+            asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", value_str[i], i, value_str);
+            return 1;
+        }
+    }
+    if (value_str[i] != 'T') {
+        asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", 'T' expected.", value_str[i], i, value_str);
+        return 1;
+    }
+    ++i;
+
+    /* hours */
+    tm.tm_hour = atoi(value_str + i);
+    for (j = i + 2; i < j; ++i) {
+        if (!isdigit(value_str[i])) {
+            asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", value_str[i], i, value_str);
+            return 1;
+        }
+    }
+    if (value_str[i] != ':') {
+        asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", ':' expected.", value_str[i], i, value_str);
+        return 1;
+    }
+    ++i;
+
+    /* minutes */
+    tm.tm_min = atoi(value_str + i);
+    for (j = i + 2; i < j; ++i) {
+        if (!isdigit(value_str[i])) {
+            asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", value_str[i], i, value_str);
+            return 1;
+        }
+    }
+    if (value_str[i] != ':') {
+        asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", ':' expected.", value_str[i], i, value_str);
+        return 1;
+    }
+    ++i;
+
+    /* seconds */
+    tm.tm_sec = atoi(value_str + i);
+    for (j = i + 2; i < j; ++i) {
+        if (!isdigit(value_str[i])) {
+            asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", value_str[i], i, value_str);
+            return 1;
+        }
+    }
+    if ((value_str[i] != '.') && (value_str[i] != 'Z') && (value_str[i] != '+') && (value_str[i] != '-')) {
+        asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", '.', 'Z', '+', or '-' expected.",
+                 value_str[i], i, value_str);
+        return 1;
+    }
+
+    /* validate using mktime() */
+    tm2 = tm;
+    if (mktime(&tm) == -1) {
+        asprintf(err_msg, "Checking date-and-time value \"%s\" failed (%s).", value_str, strerror(errno));
+        return 1;
+    }
+    /* we now have correctly filled the remaining values, use them */
+    memcpy(((char *)&tm2) + (6 * sizeof(int)), ((char *)&tm) + (6 * sizeof(int)), sizeof(struct tm) - (6 * sizeof(int)));
+    /* back it up again */
+    tm = tm2;
+    /* let mktime() correct date & time with having the other values correct now */
+    if (mktime(&tm) == -1) {
+        asprintf(err_msg, "Checking date-and-time value \"%s\" failed (%s).", value_str, strerror(errno));
+        return 1;
+    }
+    /* detect changes in the filled values */
+    if (memcmp(&tm, &tm2, 6 * sizeof(int))) {
+        asprintf(err_msg, "Checking date-and-time value \"%s\" failed, canonical date and time is \"%04d-%02d-%02dT%02d:%02d:%02d\".",
+                 value_str, tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+        return 1;
+    }
+
+    /* tenth of a second */
+    if (value_str[i] == '.') {
+        ++i;
+        if (!isdigit(value_str[i])) {
+            asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", value_str[i], i, value_str);
+            return 1;
+        }
+        ++i;
+    }
+
+    switch (value_str[i]) {
+    case 'Z':
+        /* done */
+        break;
+    case '+':
+    case '-':
+        /* timezone shift */
+        k = sizeof gmt_offsets / sizeof *gmt_offsets;
+        for (j = 0; j < k ; ++j) {
+            if (!strncmp(value_str + i, gmt_offsets[j], 6)) {
+                break;
+            }
+        }
+        if (j == k) {
+            asprintf(err_msg, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".", value_str + i, value_str);
+            return 1;
+        }
+        i += 5;
+        break;
+    default:
+        /* unreachable */
+        return 1;
+    }
+
+    /* no other characters expected */
+    ++i;
+    if (value_str[i]) {
+        asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", no characters expected.", value_str[i], i, value_str);
+        return 1;
+    }
+
+    /* validation succeeded and we do not want to change how it is stored */
+    return 0;
+}
+
+/* Name of this array must match the file name! */
+struct lytype_plugin_list user_date_and_time[] = {
+    {"ietf-yang-types", "2013-07-15", "date-and-time", date_and_time_store_clb, NULL},
+    {NULL, NULL, NULL, NULL, NULL} /* terminating item */
+};
diff --git a/src/user_types/user_ipv4.c b/src/user_types/user_ipv4.c
index 891a6b4..367597f 100644
--- a/src/user_types/user_ipv4.c
+++ b/src/user_types/user_ipv4.c
@@ -1,7 +1,7 @@
 /**
- * @file user_ipv4.h
+ * @file user_ipv4.c
  * @author Michal Vasko <mvasko@cesnet.cz>
- * @brief example implementation of an ipv4-address as a user type
+ * @brief Example implementation of an ipv4-address as a user type
  *
  * Copyright (c) 2018 CESNET, z.s.p.o.
  *