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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include "libudev.h"

/* We want to check deprecated symbols too, without complaining */
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"


struct symbol {
        const char *name;
        const void *symbol;
};
static struct symbol symbols_from_sym[] = {
        { "udev_device_get_action", udev_device_get_action },
        { "udev_device_get_devlinks_list_entry", udev_device_get_devlinks_list_entry },
        { "udev_device_get_devnode", udev_device_get_devnode },
        { "udev_device_get_devnum", udev_device_get_devnum },
        { "udev_device_get_devpath", udev_device_get_devpath },
        { "udev_device_get_devtype", udev_device_get_devtype },
        { "udev_device_get_driver", udev_device_get_driver },
        { "udev_device_get_is_initialized", udev_device_get_is_initialized },
        { "udev_device_get_parent", udev_device_get_parent },
        { "udev_device_get_parent_with_subsystem_devtype", udev_device_get_parent_with_subsystem_devtype },
        { "udev_device_get_properties_list_entry", udev_device_get_properties_list_entry },
        { "udev_device_get_property_value", udev_device_get_property_value },
        { "udev_device_get_seqnum", udev_device_get_seqnum },
        { "udev_device_get_subsystem", udev_device_get_subsystem },
        { "udev_device_get_sysattr_list_entry", udev_device_get_sysattr_list_entry },
        { "udev_device_get_sysattr_value", udev_device_get_sysattr_value },
        { "udev_device_get_sysname", udev_device_get_sysname },
        { "udev_device_get_sysnum", udev_device_get_sysnum },
        { "udev_device_get_syspath", udev_device_get_syspath },
        { "udev_device_get_tags_list_entry", udev_device_get_tags_list_entry },
        { "udev_device_get_udev", udev_device_get_udev },
        { "udev_device_get_usec_since_initialized", udev_device_get_usec_since_initialized },
        { "udev_device_has_tag", udev_device_has_tag },
        { "udev_device_new_from_devnum", udev_device_new_from_devnum },
        { "udev_device_new_from_environment", udev_device_new_from_environment },
        { "udev_device_new_from_subsystem_sysname", udev_device_new_from_subsystem_sysname },
        { "udev_device_new_from_syspath", udev_device_new_from_syspath },
        { "udev_device_ref", udev_device_ref },
        { "udev_device_unref", udev_device_unref },
        { "udev_enumerate_add_match_is_initialized", udev_enumerate_add_match_is_initialized },
        { "udev_enumerate_add_match_parent", udev_enumerate_add_match_parent },
        { "udev_enumerate_add_match_property", udev_enumerate_add_match_property },
        { "udev_enumerate_add_match_subsystem", udev_enumerate_add_match_subsystem },
        { "udev_enumerate_add_match_sysattr", udev_enumerate_add_match_sysattr },
        { "udev_enumerate_add_match_sysname", udev_enumerate_add_match_sysname },
        { "udev_enumerate_add_match_tag", udev_enumerate_add_match_tag },
        { "udev_enumerate_add_nomatch_subsystem", udev_enumerate_add_nomatch_subsystem },
        { "udev_enumerate_add_nomatch_sysattr", udev_enumerate_add_nomatch_sysattr },
        { "udev_enumerate_add_syspath", udev_enumerate_add_syspath },
        { "udev_enumerate_get_list_entry", udev_enumerate_get_list_entry },
        { "udev_enumerate_get_udev", udev_enumerate_get_udev },
        { "udev_enumerate_new", udev_enumerate_new },
        { "udev_enumerate_ref", udev_enumerate_ref },
        { "udev_enumerate_scan_devices", udev_enumerate_scan_devices },
        { "udev_enumerate_scan_subsystems", udev_enumerate_scan_subsystems },
        { "udev_enumerate_unref", udev_enumerate_unref },
        { "udev_get_log_priority", udev_get_log_priority },
        { "udev_get_userdata", udev_get_userdata },
        { "udev_list_entry_get_by_name", udev_list_entry_get_by_name },
        { "udev_list_entry_get_name", udev_list_entry_get_name },
        { "udev_list_entry_get_next", udev_list_entry_get_next },
        { "udev_list_entry_get_value", udev_list_entry_get_value },
        { "udev_monitor_enable_receiving", udev_monitor_enable_receiving },
        { "udev_monitor_filter_add_match_subsystem_devtype", udev_monitor_filter_add_match_subsystem_devtype },
        { "udev_monitor_filter_add_match_tag", udev_monitor_filter_add_match_tag },
        { "udev_monitor_filter_remove", udev_monitor_filter_remove },
        { "udev_monitor_filter_update", udev_monitor_filter_update },
        { "udev_monitor_get_fd", udev_monitor_get_fd },
        { "udev_monitor_get_udev", udev_monitor_get_udev },
        { "udev_monitor_new_from_netlink", udev_monitor_new_from_netlink },
        { "udev_monitor_receive_device", udev_monitor_receive_device },
        { "udev_monitor_ref", udev_monitor_ref },
        { "udev_monitor_set_receive_buffer_size", udev_monitor_set_receive_buffer_size },
        { "udev_monitor_unref", udev_monitor_unref },
        { "udev_new", udev_new },
        { "udev_queue_get_kernel_seqnum", udev_queue_get_kernel_seqnum },
        { "udev_queue_get_queue_is_empty", udev_queue_get_queue_is_empty },
        { "udev_queue_get_queued_list_entry", udev_queue_get_queued_list_entry },
        { "udev_queue_get_seqnum_is_finished", udev_queue_get_seqnum_is_finished },
        { "udev_queue_get_seqnum_sequence_is_finished", udev_queue_get_seqnum_sequence_is_finished },
        { "udev_queue_get_udev", udev_queue_get_udev },
        { "udev_queue_get_udev_is_active", udev_queue_get_udev_is_active },
        { "udev_queue_get_udev_seqnum", udev_queue_get_udev_seqnum },
        { "udev_queue_new", udev_queue_new },
        { "udev_queue_ref", udev_queue_ref },
        { "udev_queue_unref", udev_queue_unref },
        { "udev_ref", udev_ref },
        { "udev_set_log_fn", udev_set_log_fn },
        { "udev_set_log_priority", udev_set_log_priority },
        { "udev_set_userdata", udev_set_userdata },
        { "udev_unref", udev_unref },
        { "udev_util_encode_string", udev_util_encode_string },
        { "udev_device_new_from_device_id", udev_device_new_from_device_id },
        { "udev_hwdb_new", udev_hwdb_new },
        { "udev_hwdb_ref", udev_hwdb_ref },
        { "udev_hwdb_unref", udev_hwdb_unref },
        { "udev_hwdb_get_properties_list_entry", udev_hwdb_get_properties_list_entry },
        { "udev_device_set_sysattr_value", udev_device_set_sysattr_value },
        { "udev_queue_flush", udev_queue_flush },
        { "udev_queue_get_fd", udev_queue_get_fd },
        { "udev_device_has_current_tag", udev_device_has_current_tag },
        { "udev_device_get_current_tags_list_entry", udev_device_get_current_tags_list_entry },
        {}
}, symbols_from_header[] = {
        { "udev_ref", udev_ref },
        { "udev_unref", udev_unref },
        { "udev_new", udev_new },
        { "udev_set_log_fn", udev_set_log_fn },
        { "udev_get_log_priority", udev_get_log_priority },
        { "udev_set_log_priority", udev_set_log_priority },
        { "udev_get_userdata", udev_get_userdata },
        { "udev_set_userdata", udev_set_userdata },
        { "udev_list_entry_get_next", udev_list_entry_get_next },
        { "udev_list_entry_get_by_name", udev_list_entry_get_by_name },
        { "udev_list_entry_get_name", udev_list_entry_get_name },
        { "udev_list_entry_get_value", udev_list_entry_get_value },
        { "udev_device_ref", udev_device_ref },
        { "udev_device_unref", udev_device_unref },
        { "udev_device_get_udev", udev_device_get_udev },
        { "udev_device_new_from_syspath", udev_device_new_from_syspath },
        { "udev_device_new_from_devnum", udev_device_new_from_devnum },
        { "udev_device_new_from_subsystem_sysname", udev_device_new_from_subsystem_sysname },
        { "udev_device_new_from_device_id", udev_device_new_from_device_id },
        { "udev_device_new_from_environment", udev_device_new_from_environment },
        { "udev_device_get_parent", udev_device_get_parent },
        { "udev_device_get_parent_with_subsystem_devtype", udev_device_get_parent_with_subsystem_devtype },
        { "udev_device_get_devpath", udev_device_get_devpath },
        { "udev_device_get_subsystem", udev_device_get_subsystem },
        { "udev_device_get_devtype", udev_device_get_devtype },
        { "udev_device_get_syspath", udev_device_get_syspath },
        { "udev_device_get_sysname", udev_device_get_sysname },
        { "udev_device_get_sysnum", udev_device_get_sysnum },
        { "udev_device_get_devnode", udev_device_get_devnode },
        { "udev_device_get_is_initialized", udev_device_get_is_initialized },
        { "udev_device_get_devlinks_list_entry", udev_device_get_devlinks_list_entry },
        { "udev_device_get_properties_list_entry", udev_device_get_properties_list_entry },
        { "udev_device_get_tags_list_entry", udev_device_get_tags_list_entry },
        { "udev_device_get_current_tags_list_entry", udev_device_get_current_tags_list_entry },
        { "udev_device_get_sysattr_list_entry", udev_device_get_sysattr_list_entry },
        { "udev_device_get_property_value", udev_device_get_property_value },
        { "udev_device_get_driver", udev_device_get_driver },
        { "udev_device_get_devnum", udev_device_get_devnum },
        { "udev_device_get_action", udev_device_get_action },
        { "udev_device_get_seqnum", udev_device_get_seqnum },
        { "udev_device_get_usec_since_initialized", udev_device_get_usec_since_initialized },
        { "udev_device_get_sysattr_value", udev_device_get_sysattr_value },
        { "udev_device_set_sysattr_value", udev_device_set_sysattr_value },
        { "udev_device_has_tag", udev_device_has_tag },
        { "udev_device_has_current_tag", udev_device_has_current_tag },
        { "udev_monitor_ref", udev_monitor_ref },
        { "udev_monitor_unref", udev_monitor_unref },
        { "udev_monitor_get_udev", udev_monitor_get_udev },
        { "udev_monitor_new_from_netlink", udev_monitor_new_from_netlink },
        { "udev_monitor_enable_receiving", udev_monitor_enable_receiving },
        { "udev_monitor_set_receive_buffer_size", udev_monitor_set_receive_buffer_size },
        { "udev_monitor_get_fd", udev_monitor_get_fd },
        { "udev_monitor_receive_device", udev_monitor_receive_device },
        { "udev_monitor_filter_add_match_subsystem_devtype", udev_monitor_filter_add_match_subsystem_devtype },
        { "udev_monitor_filter_add_match_tag", udev_monitor_filter_add_match_tag },
        { "udev_monitor_filter_update", udev_monitor_filter_update },
        { "udev_monitor_filter_remove", udev_monitor_filter_remove },
        { "udev_enumerate_ref", udev_enumerate_ref },
        { "udev_enumerate_unref", udev_enumerate_unref },
        { "udev_enumerate_get_udev", udev_enumerate_get_udev },
        { "udev_enumerate_new", udev_enumerate_new },
        { "udev_enumerate_add_match_subsystem", udev_enumerate_add_match_subsystem },
        { "udev_enumerate_add_nomatch_subsystem", udev_enumerate_add_nomatch_subsystem },
        { "udev_enumerate_add_match_sysattr", udev_enumerate_add_match_sysattr },
        { "udev_enumerate_add_nomatch_sysattr", udev_enumerate_add_nomatch_sysattr },
        { "udev_enumerate_add_match_property", udev_enumerate_add_match_property },
        { "udev_enumerate_add_match_sysname", udev_enumerate_add_match_sysname },
        { "udev_enumerate_add_match_tag", udev_enumerate_add_match_tag },
        { "udev_enumerate_add_match_parent", udev_enumerate_add_match_parent },
        { "udev_enumerate_add_match_is_initialized", udev_enumerate_add_match_is_initialized },
        { "udev_enumerate_add_syspath", udev_enumerate_add_syspath },
        { "udev_enumerate_scan_devices", udev_enumerate_scan_devices },
        { "udev_enumerate_scan_subsystems", udev_enumerate_scan_subsystems },
        { "udev_enumerate_get_list_entry", udev_enumerate_get_list_entry },
        { "udev_queue_ref", udev_queue_ref },
        { "udev_queue_unref", udev_queue_unref },
        { "udev_queue_get_udev", udev_queue_get_udev },
        { "udev_queue_new", udev_queue_new },
        { "udev_queue_get_kernel_seqnum", udev_queue_get_kernel_seqnum },
        { "udev_queue_get_udev_seqnum", udev_queue_get_udev_seqnum },
        { "udev_queue_get_udev_is_active", udev_queue_get_udev_is_active },
        { "udev_queue_get_queue_is_empty", udev_queue_get_queue_is_empty },
        { "udev_queue_get_seqnum_is_finished", udev_queue_get_seqnum_is_finished },
        { "udev_queue_get_seqnum_sequence_is_finished", udev_queue_get_seqnum_sequence_is_finished },
        { "udev_queue_get_fd", udev_queue_get_fd },
        { "udev_queue_flush", udev_queue_flush },
        { "udev_queue_get_queued_list_entry", udev_queue_get_queued_list_entry },
        { "udev_hwdb_new", udev_hwdb_new },
        { "udev_hwdb_ref", udev_hwdb_ref },
        { "udev_hwdb_unref", udev_hwdb_unref },
        { "udev_hwdb_get_properties_list_entry", udev_hwdb_get_properties_list_entry },
        { "udev_util_encode_string", udev_util_encode_string },
        {}
}, symbols_from_source[] = {
        { "udev_device_get_seqnum", udev_device_get_seqnum },
        { "udev_device_get_devnum", udev_device_get_devnum },
        { "udev_device_get_driver", udev_device_get_driver },
        { "udev_device_get_devtype", udev_device_get_devtype },
        { "udev_device_get_subsystem", udev_device_get_subsystem },
        { "udev_device_get_property_value", udev_device_get_property_value },
        { "udev_device_new_from_syspath", udev_device_new_from_syspath },
        { "udev_device_new_from_devnum", udev_device_new_from_devnum },
        { "udev_device_new_from_device_id", udev_device_new_from_device_id },
        { "udev_device_new_from_subsystem_sysname", udev_device_new_from_subsystem_sysname },
        { "udev_device_new_from_environment", udev_device_new_from_environment },
        { "udev_device_get_parent", udev_device_get_parent },
        { "udev_device_get_parent_with_subsystem_devtype", udev_device_get_parent_with_subsystem_devtype },
        { "udev_device_get_udev", udev_device_get_udev },
        { "udev_device_ref", udev_device_ref },
        { "udev_device_unref", udev_device_unref },
        { "udev_device_get_devpath", udev_device_get_devpath },
        { "udev_device_get_syspath", udev_device_get_syspath },
        { "udev_device_get_sysname", udev_device_get_sysname },
        { "udev_device_get_sysnum", udev_device_get_sysnum },
        { "udev_device_get_devnode", udev_device_get_devnode },
        { "udev_device_get_devlinks_list_entry", udev_device_get_devlinks_list_entry },
        { "udev_device_get_properties_list_entry", udev_device_get_properties_list_entry },
        { "udev_device_get_action", udev_device_get_action },
        { "udev_device_get_usec_since_initialized", udev_device_get_usec_since_initialized },
        { "udev_device_get_sysattr_value", udev_device_get_sysattr_value },
        { "udev_device_set_sysattr_value", udev_device_set_sysattr_value },
        { "udev_device_get_sysattr_list_entry", udev_device_get_sysattr_list_entry },
        { "udev_device_get_is_initialized", udev_device_get_is_initialized },
        { "udev_device_get_tags_list_entry", udev_device_get_tags_list_entry },
        { "udev_device_get_current_tags_list_entry", udev_device_get_current_tags_list_entry },
        { "udev_device_has_tag", udev_device_has_tag },
        { "udev_device_has_current_tag", udev_device_has_current_tag },
        { "udev_enumerate_new", udev_enumerate_new },
        { "udev_enumerate_ref", udev_enumerate_ref },
        { "udev_enumerate_unref", udev_enumerate_unref },
        { "udev_enumerate_get_udev", udev_enumerate_get_udev },
        { "udev_enumerate_get_list_entry", udev_enumerate_get_list_entry },
        { "udev_enumerate_add_match_subsystem", udev_enumerate_add_match_subsystem },
        { "udev_enumerate_add_nomatch_subsystem", udev_enumerate_add_nomatch_subsystem },
        { "udev_enumerate_add_match_sysattr", udev_enumerate_add_match_sysattr },
        { "udev_enumerate_add_nomatch_sysattr", udev_enumerate_add_nomatch_sysattr },
        { "udev_enumerate_add_match_property", udev_enumerate_add_match_property },
        { "udev_enumerate_add_match_tag", udev_enumerate_add_match_tag },
        { "udev_enumerate_add_match_parent", udev_enumerate_add_match_parent },
        { "udev_enumerate_add_match_is_initialized", udev_enumerate_add_match_is_initialized },
        { "udev_enumerate_add_match_sysname", udev_enumerate_add_match_sysname },
        { "udev_enumerate_add_syspath", udev_enumerate_add_syspath },
        { "udev_enumerate_scan_devices", udev_enumerate_scan_devices },
        { "udev_enumerate_scan_subsystems", udev_enumerate_scan_subsystems },
        { "udev_hwdb_new", udev_hwdb_new },
        { "udev_hwdb_ref", udev_hwdb_ref },
        { "udev_hwdb_unref", udev_hwdb_unref },
        { "udev_hwdb_get_properties_list_entry", udev_hwdb_get_properties_list_entry },
        { "udev_list_entry_get_next", udev_list_entry_get_next },
        { "udev_list_entry_get_by_name", udev_list_entry_get_by_name },
        { "udev_list_entry_get_name", udev_list_entry_get_name },
        { "udev_list_entry_get_value", udev_list_entry_get_value },
        { "udev_monitor_new_from_netlink", udev_monitor_new_from_netlink },
        { "udev_monitor_filter_update", udev_monitor_filter_update },
        { "udev_monitor_enable_receiving", udev_monitor_enable_receiving },
        { "udev_monitor_set_receive_buffer_size", udev_monitor_set_receive_buffer_size },
        { "udev_monitor_ref", udev_monitor_ref },
        { "udev_monitor_unref", udev_monitor_unref },
        { "udev_monitor_get_udev", udev_monitor_get_udev },
        { "udev_monitor_get_fd", udev_monitor_get_fd },
        { "udev_monitor_receive_device", udev_monitor_receive_device },
        { "udev_monitor_filter_add_match_subsystem_devtype", udev_monitor_filter_add_match_subsystem_devtype },
        { "udev_monitor_filter_add_match_tag", udev_monitor_filter_add_match_tag },
        { "udev_monitor_filter_remove", udev_monitor_filter_remove },
        { "udev_queue_new", udev_queue_new },
        { "udev_queue_ref", udev_queue_ref },
        { "udev_queue_unref", udev_queue_unref },
        { "udev_queue_get_udev", udev_queue_get_udev },
        { "udev_queue_get_kernel_seqnum", udev_queue_get_kernel_seqnum },
        { "udev_queue_get_udev_seqnum", udev_queue_get_udev_seqnum },
        { "udev_queue_get_udev_is_active", udev_queue_get_udev_is_active },
        { "udev_queue_get_queue_is_empty", udev_queue_get_queue_is_empty },
        { "udev_queue_get_seqnum_sequence_is_finished", udev_queue_get_seqnum_sequence_is_finished },
        { "udev_queue_get_seqnum_is_finished", udev_queue_get_seqnum_is_finished },
        { "udev_queue_get_queued_list_entry", udev_queue_get_queued_list_entry },
        { "udev_queue_get_fd", udev_queue_get_fd },
        { "udev_queue_flush", udev_queue_flush },
        { "udev_util_encode_string", udev_util_encode_string },
        { "udev_get_userdata", udev_get_userdata },
        { "udev_set_userdata", udev_set_userdata },
        { "udev_new", udev_new },
        { "udev_ref", udev_ref },
        { "udev_unref", udev_unref },
        { "udev_set_log_fn", udev_set_log_fn },
        { "udev_get_log_priority", udev_get_log_priority },
        { "udev_set_log_priority", udev_set_log_priority },
        {}
};

static int sort_callback(const void *a, const void *b) {
        const struct symbol *x = a, *y = b;
        return strcmp(x->name, y->name);
}

int main(void) {
        size_t size = sizeof(symbols_from_sym[0]),
                n_sym = sizeof(symbols_from_sym)/sizeof(symbols_from_sym[0]) - 1,
                n_header = sizeof(symbols_from_header)/sizeof(symbols_from_header[0]) - 1,
                n_source = sizeof(symbols_from_source)/sizeof(symbols_from_source[0]) - 1;

        qsort(symbols_from_sym, n_sym, size, sort_callback);
        qsort(symbols_from_header, n_header, size, sort_callback);
        qsort(symbols_from_source, n_source, size, sort_callback);

        puts("From symbol file:");
        for (size_t i = 0; i < n_sym; i++)
                printf("%p: %s\n", symbols_from_sym[i].symbol, symbols_from_sym[i].name);

        puts("\nFrom header files:");
        for (size_t i = 0; i < n_header; i++)
                printf("%p: %s\n", symbols_from_header[i].symbol, symbols_from_header[i].name);

        puts("\nFrom source files:");
        for (size_t i = 0; i < n_source; i++)
                printf("%p: %s\n", symbols_from_source[i].symbol, symbols_from_source[i].name);

        puts("");
        printf("Found %zu symbols from symbol file.\n", n_sym);
        printf("Found %zu symbols from header files.\n", n_header);
        printf("Found %zu symbols from source files.\n", n_source);

        unsigned n_error = 0;

        for (size_t i = 0; i < n_sym; i++) {
                if (!bsearch(symbols_from_sym+i, symbols_from_header, n_header, size, sort_callback)) {
                        printf("Found in symbol file, but not in headers: %s\n", symbols_from_sym[i].name);
                        n_error++;
                }
                if (!bsearch(symbols_from_sym+i, symbols_from_source, n_source, size, sort_callback)) {
                        printf("Found in symbol file, but not in sources: %s\n", symbols_from_sym[i].name);
                        n_error++;
                }
        }

        for (size_t i = 0; i < n_header; i++) {
                if (!bsearch(symbols_from_header+i, symbols_from_sym, n_sym, size, sort_callback)) {
                        printf("Found in header file, but not in symbol file: %s\n", symbols_from_header[i].name);
                        n_error++;
                }
                if (!bsearch(symbols_from_header+i, symbols_from_source, n_source, size, sort_callback)) {
                        printf("Found in header file, but not in sources: %s\n", symbols_from_header[i].name);
                        n_error++;
                }
        }

        for (size_t i = 0; i < n_source; i++) {
                if (!bsearch(symbols_from_source+i, symbols_from_sym, n_sym, size, sort_callback)) {
                        printf("Found in source file, but not in symbol file: %s\n", symbols_from_source[i].name);
                        n_error++;
                }
                if (!bsearch(symbols_from_source+i, symbols_from_header, n_header, size, sort_callback)) {
                        printf("Found in source file, but not in header: %s\n", symbols_from_source[i].name);
                        n_error++;
                }
        }

        return n_error == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
