/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "analyze.h"
#include "analyze-capability.h"
#include "capability-list.h"
#include "capability-util.h"
#include "format-table.h"
#include "log.h"
#include "parse-util.h"

static int table_add_capability(Table *table, int c) {
        int r;

        r = table_add_many(table,
                           TABLE_STRING, capability_to_name(c) ?: "cap_???",
                           TABLE_UINT, c);
        if (r < 0)
                return table_log_add_error(r);
        return 0;
}

int verb_capabilities(int argc, char *argv[], void *userdata) {
        _cleanup_(table_unrefp) Table *table = NULL;
        unsigned last_cap;
        int r;

        table = table_new("name", "number");
        if (!table)
                return log_oom();

        (void) table_set_align_percent(table, table_get_cell(table, 0, 1), 100);

        /* Determine the maximum of the last cap known by the kernel and by us */
        last_cap = MAX((unsigned) CAP_LAST_CAP, cap_last_cap());

        if (arg_capability == CAPABILITY_MASK) {
                uint64_t cap_mask;

                if (argc != 2)
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Exactly 1 positional argument expected.");

                r = safe_atoux64(argv[1], &cap_mask);
                if (r < 0)
                        return log_error_errno(r, "Capability mask \"%s\" is not valid.", argv[1]);

                for (unsigned c = 0; cap_mask != 0; ) {
                        if (cap_mask & 0b1) {
                                if (c > last_cap)
                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Capability %u is not known.", c);

                                r = table_add_capability(table, c);
                                if (r < 0)
                                        return r;
                        }
                        ++c;
                        cap_mask >>= 1;
                }

                (void) table_set_sort(table, (size_t) 1);

        } else if (argc == 1) {
                for (unsigned c = 0; c <= last_cap; c++) {
                        r = table_add_capability(table, c);
                        if (r < 0)
                                return r;
                }
        } else {
                for (int i = 1; i < argc; i++) {
                        int c;

                        c = capability_from_name(argv[i]);
                        if (c < 0 || (unsigned) c > last_cap)
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Capability \"%s\" is not known.", argv[i]);

                        r = table_add_capability(table, c);
                        if (r < 0)
                                return r;
                }

                (void) table_set_sort(table, (size_t) 1);
        }

        r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
        if (r < 0)
                return r;

        return 0;
}
