yanglint REFACTOR schema_features in separate file
diff --git a/tools/lint/CMakeLists.txt b/tools/lint/CMakeLists.txt
index 9ff39b2..f333bef 100644
--- a/tools/lint/CMakeLists.txt
+++ b/tools/lint/CMakeLists.txt
@@ -22,6 +22,7 @@
     cmd_verb.c
     cmd_debug.c
     yl_opt.c
+    yl_schema_features.c
     common.c
 )
 if(YANGLINT_INTERACTIVE)
diff --git a/tools/lint/cmd_add.c b/tools/lint/cmd_add.c
index 3b2acd4..6a9af8d 100644
--- a/tools/lint/cmd_add.c
+++ b/tools/lint/cmd_add.c
@@ -28,6 +28,7 @@
 
 #include "common.h"
 #include "yl_opt.h"
+#include "yl_schema_features.h"
 
 void
 cmd_add_help(void)
diff --git a/tools/lint/cmd_feature.c b/tools/lint/cmd_feature.c
index 09f8a29..d204c90 100644
--- a/tools/lint/cmd_feature.c
+++ b/tools/lint/cmd_feature.c
@@ -25,6 +25,7 @@
 
 #include "common.h"
 #include "yl_opt.h"
+#include "yl_schema_features.h"
 
 void
 cmd_feature_help(void)
diff --git a/tools/lint/cmd_load.c b/tools/lint/cmd_load.c
index fe56d84..f7457c8 100644
--- a/tools/lint/cmd_load.c
+++ b/tools/lint/cmd_load.c
@@ -28,6 +28,7 @@
 
 #include "common.h"
 #include "yl_opt.h"
+#include "yl_schema_features.h"
 
 void
 cmd_load_help(void)
diff --git a/tools/lint/common.c b/tools/lint/common.c
index 44dd9b4..bdf96e6 100644
--- a/tools/lint/common.c
+++ b/tools/lint/common.c
@@ -20,7 +20,6 @@
 
 #include <assert.h>
 #include <errno.h>
-#include <getopt.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -88,245 +87,6 @@
     return 0;
 }
 
-void
-free_features(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, ly_bool 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;
-}
-
 LYS_INFORMAT
 get_schema_format(const char *filename)
 {
diff --git a/tools/lint/common.h b/tools/lint/common.h
index 0684daa..340cecd 100644
--- a/tools/lint/common.h
+++ b/tools/lint/common.h
@@ -59,82 +59,6 @@
 struct cmdline_file;
 
 /**
- * @brief Storage for the list of the features (their names) in a specific YANG module.
- */
-struct schema_features {
-    char *mod_name;
-    char **features;
-    ly_bool applied;
-};
-
-/**
- * @brief Free the schema features list (struct schema_features *)
- * @param[in,out] flist The (struct schema_features *) to free.
- */
-void free_features(void *flist);
-
-/**
- * @brief Get the list of features connected with the specific YANG module.
- *
- * @param[in] fset The set of features information (struct schema_features *).
- * @param[in] module Name of the YANG module which features should be found.
- * @param[out] features Pointer to the list of features being returned.
- */
-void get_features(const struct ly_set *fset, const char *module, const char ***features);
-
-/**
- * @brief Parse features being specified for the specific YANG module.
- *
- * Format of the input @p fstring is as follows: "<module_name>:[<feature>,]*"
- *
- * @param[in] fstring Input string to be parsed.
- * @param[in, out] fset Features information set (of struct schema_features *). The set is being filled.
- */
-int parse_features(const char *fstring, struct ly_set *fset);
-
-/**
- * @brief Collect all features of a module.
- *
- * @param[in] mod Module to be searched for features.
- * @param[out] set Set in which the features will be stored.
- * @return 0 on success.
- * @return 1 on error.
- */
-int collect_features(const struct lys_module *mod, struct ly_set *set);
-
-/**
- * @brief Print all features of a single module.
- *
- * @param[in] out The output handler for printing.
- * @param[in] mod Module which contains the features.
- * @param[in] set Set which holds the features.
- */
-void print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set);
-
-/**
- * @brief Generate a string, which will contain features paramater.
- *
- * @param[in] mod Module, for which the string will be generated.
- * @param[in] set Set containing the features.
- * @param[out] features_param String which will contain the output.
- * @return 0 on success.
- * @return 1 on error.
- */
-int generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param);
-
-/**
- * @brief Print all features of all implemented modules.
- *
- * @param[in] out The output handler for printing.
- * @param[in] ctx Libyang context.
- * @param[in] generate_features Flag expressing whether to generate features parameter.
- * @param[out] features_param String, which will contain the output if the above flag is set.
- * @return 0 on success.
- * @return 1 on error.
- */
-int print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool generate_features, char **features_param);
-
-/**
  * @brief Parse path of a schema module file into the directory and module name.
  *
  * @param[in] path Schema module file path to be parsed.
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index b7d821f..71a0639 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -32,6 +32,7 @@
 #include "out.h"
 #include "tools/config.h"
 #include "yl_opt.h"
+#include "yl_schema_features.h"
 
 static void
 version(void)
@@ -274,7 +275,7 @@
 {
     if (yang_lib_file) {
         /* ignore features */
-        ly_set_erase(schema_features, free_features);
+        ly_set_erase(schema_features, yl_schema_features_free);
 
         if (ly_ctx_new_ylpath(searchpaths, yang_lib_file, LYD_UNKNOWN, *ctx_options, ctx)) {
             YLMSG_E("Unable to modify libyang context with yang-library data.\n");
diff --git a/tools/lint/yl_opt.c b/tools/lint/yl_opt.c
index 20fbf1f..67aff0e 100644
--- a/tools/lint/yl_opt.c
+++ b/tools/lint/yl_opt.c
@@ -23,6 +23,7 @@
 
 #include "common.h"
 #include "yl_opt.h"
+#include "yl_schema_features.h"
 
 struct cmdline_file *
 fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
@@ -79,7 +80,7 @@
     ly_set_erase(&yo->data_xpath, NULL);
 
     /* schema */
-    ly_set_erase(&yo->schema_features, free_features);
+    ly_set_erase(&yo->schema_features, yl_schema_features_free);
     ly_set_erase(&yo->schema_modules, NULL);
     free(yo->features_output);
 
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;
+}
diff --git a/tools/lint/yl_schema_features.h b/tools/lint/yl_schema_features.h
new file mode 100644
index 0000000..b697dcc
--- /dev/null
+++ b/tools/lint/yl_schema_features.h
@@ -0,0 +1,102 @@
+/**
+ * @file yl_schema_features.h
+ * @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
+ */
+
+#ifndef YL_SCHEMA_FEATURES_H_
+#define YL_SCHEMA_FEATURES_H_
+
+#include <stdint.h>
+
+struct ly_set;
+struct lys_module;
+struct ly_out;
+struct ly_ctx;
+
+/**
+ * @brief Storage for the list of the features (their names) in a specific YANG module.
+ */
+struct schema_features {
+    char *mod_name;
+    char **features;
+    uint8_t applied;
+};
+
+/**
+ * @brief Free the schema features list (struct schema_features *)
+ * @param[in,out] flist The (struct schema_features *) to free.
+ */
+void yl_schema_features_free(void *flist);
+
+/**
+ * @brief Get the list of features connected with the specific YANG module.
+ *
+ * @param[in] fset The set of features information (struct schema_features *).
+ * @param[in] module Name of the YANG module which features should be found.
+ * @param[out] features Pointer to the list of features being returned.
+ */
+void get_features(const struct ly_set *fset, const char *module, const char ***features);
+
+/**
+ * @brief Parse features being specified for the specific YANG module.
+ *
+ * Format of the input @p fstring is as follows: "<module_name>:[<feature>,]*"
+ *
+ * @param[in] fstring Input string to be parsed.
+ * @param[in, out] fset Features information set (of struct schema_features *). The set is being filled.
+ */
+int parse_features(const char *fstring, struct ly_set *fset);
+
+/**
+ * @brief Collect all features of a module.
+ *
+ * @param[in] mod Module to be searched for features.
+ * @param[out] set Set in which the features will be stored.
+ * @return 0 on success.
+ * @return 1 on error.
+ */
+int collect_features(const struct lys_module *mod, struct ly_set *set);
+
+/**
+ * @brief Print all features of a single module.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] mod Module which contains the features.
+ * @param[in] set Set which holds the features.
+ */
+void print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set);
+
+/**
+ * @brief Generate a string, which will contain features paramater.
+ *
+ * @param[in] mod Module, for which the string will be generated.
+ * @param[in] set Set containing the features.
+ * @param[out] features_param String which will contain the output.
+ * @return 0 on success.
+ * @return 1 on error.
+ */
+int generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param);
+
+/**
+ * @brief Print all features of all implemented modules.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] ctx Libyang context.
+ * @param[in] generate_features Flag expressing whether to generate features parameter.
+ * @param[out] features_param String, which will contain the output if the above flag is set.
+ * @return 0 on success.
+ * @return 1 on error.
+ */
+int print_all_features(struct ly_out *out, const struct ly_ctx *ctx, uint8_t generate_features, char **features_param);
+
+
+#endif /* YL_SCHEMA_FEATURES_H_ */