yanglint REFACTOR schema_features in separate file
diff --git a/tools/lint/yl_schema_features.c b/tools/lint/yl_schema_features.c
new file mode 100644
index 0000000..52c75e7
--- /dev/null
+++ b/tools/lint/yl_schema_features.c
@@ -0,0 +1,264 @@
+/**
+ * @file yl_schema_features.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief Control features for the schema.
+ *
+ * Copyright (c) 2023 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 _GNU_SOURCE
+
+#include <errno.h>
+#include <stdlib.h> /* calloc */
+#include <string.h> /* strcmp */
+
+#include "compat.h" /* strndup */
+#include "set.h" /* ly_set */
+
+#include "common.h"
+#include "yl_schema_features.h"
+
+void
+yl_schema_features_free(void *flist)
+{
+    struct schema_features *rec = (struct schema_features *)flist;
+
+    if (rec) {
+        free(rec->mod_name);
+        if (rec->features) {
+            for (uint32_t u = 0; rec->features[u]; ++u) {
+                free(rec->features[u]);
+            }
+            free(rec->features);
+        }
+        free(rec);
+    }
+}
+
+void
+get_features(const struct ly_set *fset, const char *module, const char ***features)
+{
+    /* get features list for this module */
+    for (uint32_t u = 0; u < fset->count; ++u) {
+        struct schema_features *sf = (struct schema_features *)fset->objs[u];
+
+        if (!strcmp(module, sf->mod_name)) {
+            /* matched module - explicitly set features */
+            *features = (const char **)sf->features;
+            sf->applied = 1;
+            return;
+        }
+    }
+
+    /* features not set so disable all */
+    *features = NULL;
+}
+
+int
+parse_features(const char *fstring, struct ly_set *fset)
+{
+    struct schema_features *rec;
+    uint32_t count;
+    char *p, **fp;
+
+    rec = calloc(1, sizeof *rec);
+    if (!rec) {
+        YLMSG_E("Unable to allocate features information record (%s).\n", strerror(errno));
+        return -1;
+    }
+    if (ly_set_add(fset, rec, 1, NULL)) {
+        YLMSG_E("Unable to store features information (%s).\n", strerror(errno));
+        free(rec);
+        return -1;
+    }
+
+    /* fill the record */
+    p = strchr(fstring, ':');
+    if (!p) {
+        YLMSG_E("Invalid format of the features specification (%s).\n", fstring);
+        return -1;
+    }
+    rec->mod_name = strndup(fstring, p - fstring);
+
+    count = 0;
+    while (p) {
+        size_t len = 0;
+        char *token = p + 1;
+
+        p = strchr(token, ',');
+        if (!p) {
+            /* the last item, if any */
+            len = strlen(token);
+        } else {
+            len = p - token;
+        }
+
+        if (len) {
+            fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
+            if (!fp) {
+                YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
+                return -1;
+            }
+            rec->features = fp;
+            fp = &rec->features[count++]; /* array item to set */
+            (*fp) = strndup(token, len);
+        }
+    }
+
+    /* terminating NULL */
+    fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
+    if (!fp) {
+        YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
+        return -1;
+    }
+    rec->features = fp;
+    rec->features[count++] = NULL;
+
+    return 0;
+}
+
+int
+collect_features(const struct lys_module *mod, struct ly_set *set)
+{
+    struct lysp_feature *f = NULL;
+    uint32_t idx = 0;
+
+    while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
+        if (ly_set_add(set, (void *)f->name, 1, NULL)) {
+            YLMSG_E("Memory allocation failed.\n");
+            ly_set_erase(set, NULL);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+void
+print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set)
+{
+    size_t max_len;
+    uint32_t j;
+    const char *name;
+
+    /* header */
+    ly_print(out, "%s:\n", mod->name);
+
+    /* no features */
+    if (!set->count) {
+        ly_print(out, "\t(none)\n\n");
+        return;
+    }
+
+    /* get max len, so the statuses of all the features will be aligned */
+    max_len = 0;
+    for (j = 0; j < set->count; ++j) {
+        name = set->objs[j];
+        if (strlen(name) > max_len) {
+            max_len = strlen(name);
+        }
+    }
+
+    /* print features */
+    for (j = 0; j < set->count; ++j) {
+        name = set->objs[j];
+        ly_print(out, "\t%-*s (%s)\n", (int)max_len, name, lys_feature_value(mod, name) ? "off" : "on");
+    }
+
+    ly_print(out, "\n");
+}
+
+int
+generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param)
+{
+    uint32_t j;
+    /*
+     * features_len - length of all the features in the current module
+     * added_len - length of a string to be added, = features_len + extra necessary length
+     * param_len - length of the parameter before appending new string
+    */
+    size_t features_len, added_len, param_len;
+    char *tmp;
+
+    features_len = 0;
+    for (j = 0; j < set->count; j++) {
+        features_len += strlen(set->objs[j]);
+    }
+
+    if (j == 0) {
+        /* no features */
+        added_len = strlen("-F ") + strlen(mod->name) + strlen(":");
+    } else {
+        /* j = comma count, -1 because of trailing comma */
+        added_len = strlen("-F ") + strlen(mod->name) + strlen(":") + features_len + j - 1;
+    }
+
+    /* to avoid strlen(NULL) if this is the first call */
+    param_len = 0;
+    if (*features_param) {
+        param_len = strlen(*features_param);
+    }
+
+    /* +1 because of white space at the beginning */
+    tmp = realloc(*features_param, param_len + added_len + 1 + 1);
+    if (!tmp) {
+        goto error;
+    } else {
+        *features_param = tmp;
+    }
+    sprintf(*features_param + param_len, " -F %s:", mod->name);
+
+    for (j = 0; j < set->count; j++) {
+        strcat(*features_param, set->objs[j]);
+        /* no trailing comma */
+        if (j != (set->count - 1)) {
+            strcat(*features_param, ",");
+        }
+    }
+
+    return 0;
+
+error:
+    YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
+    return 1;
+}
+
+int
+print_all_features(struct ly_out *out, const struct ly_ctx *ctx, uint8_t generate_features, char **features_param)
+{
+    int ret = 0;
+    uint32_t i = 0;
+    struct lys_module *mod;
+    struct ly_set set = {0};
+
+    while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
+        /* only care about implemented modules */
+        if (!mod->implemented) {
+            continue;
+        }
+
+        /* always erase the set, so the previous module's features don't carry over to the next module's features */
+        ly_set_erase(&set, NULL);
+
+        if (collect_features(mod, &set)) {
+            ret = 1;
+            goto cleanup;
+        }
+
+        if (generate_features && generate_features_output(mod, &set, features_param)) {
+            ret = 1;
+            goto cleanup;
+        }
+        print_features(out, mod, &set);
+    }
+
+cleanup:
+    ly_set_erase(&set, NULL);
+    return ret;
+}