<commit_before>#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <map>

#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <pwd.h>
#include <dirent.h>

#include "dinit-util.h"
#include "service-constants.h"
#include "load-service.h"
#include "options-processing.h"

// dinitcheck:  utility to check Dinit configuration for correctness/lint

using string = std::string;
using string_iterator = std::string::iterator;

// prelim_dep: A preliminary (unresolved) service dependency
class prelim_dep
{
    public:
    std::string name;
    dependency_type dep_type;

    prelim_dep(const std::string &name_p, dependency_type dep_type_p)
        : name(name_p), dep_type(dep_type_p) { }
    prelim_dep(std::string &&name_p, dependency_type dep_type_p)
        : name(std::move(name_p)), dep_type(dep_type_p) { }
};

class service_record
{
public:
    service_record(std::string name, std::list<prelim_dep> dependencies_p) : dependencies(dependencies_p) {}

    std::string name;
    bool finished_loading = false;  // flag used to detect cyclic dependencies
    std::list<prelim_dep> dependencies;
};

using service_set_t = std::map<std::string, service_record *>;

service_record *load_service(service_set_t &services, const std::string &name,
        const service_dir_pathlist &service_dirs);

// Add some missing standard library functionality...
template <typename T> bool contains(std::vector<T> vec, const T& elem)
{
    return std::find(vec.begin(), vec.end(), elem) != vec.end();
}

int main(int argc, char **argv)
{
    using namespace std;

    service_dir_opt service_dir_opts;
    bool am_system_init = (getuid() == 0);

    std::vector<std::string> services_to_check;

    // Process command line
    if (argc > 1) {
        for (int i = 1; i < argc; i++) {
            if (argv[i][0] == '-') {
                if (argv[i][0] == '-') {
                    // An option...
                    if (strcmp(argv[i], "--services-dir") == 0 || strcmp(argv[i], "-d") == 0) {
                        if (++i < argc) {
                            service_dir_opts.set_specified_service_dir(argv[i]);
                        }
                        else {
                            cerr << "dinitcheck: '--services-dir' (-d) requires an argument" << endl;
                            return 1;
                        }
                    }
                }
                // TODO handle other options, err if unrecognized
            }
        }
    }

    service_dir_opts.build_paths(am_system_init);

    // Temporary, for testing:
    services_to_check.push_back("boot");

    // Load named service(s)
    std::map<std::string, service_record *> service_set;

    // - load the service, store dependencies as strings
    // - recurse

    // TODO additional: check chain-to, other lint

    for (size_t i = 0; i < services_to_check.size(); ++i) {
        const std::string &name = services_to_check[i];
        std::cout << "Checking service: " << name << "...\n";
        try {
            service_record *sr = load_service(service_set, name, service_dir_opts.get_paths());
            service_set[name] = sr;
            // add dependencies to services_to_check
            for (auto &dep : sr->dependencies) {
                if (service_set.count(dep.name) == 0 && !contains(services_to_check, dep.name)) {
                    services_to_check.push_back(dep.name);
                }
            }
        }
        catch (service_load_exc &exc) {
            std::cerr << "Unable to load service '" << name << "': " << exc.exc_description << "\n";
        }
    }

    // TODO check for circular dependencies

    return 0;
}

static void report_service_description_exc(service_description_exc &exc)
{
    std::cerr << "Service '" << exc.service_name << "': " << exc.exc_description << "\n";
}

static void report_error(std::system_error &exc, const std::string &service_name)
{
    std::cerr << "Service '" << service_name << "', error reading service description: " << exc.what() << "\n";
}
<commit_msg>dinitcheck: support specifying services to check on command line<commit_after>#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include <vector>
#include <list>
#include <map>

#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <pwd.h>
#include <dirent.h>

#include "dinit-util.h"
#include "service-constants.h"
#include "load-service.h"
#include "options-processing.h"

// dinitcheck:  utility to check Dinit configuration for correctness/lint

using string = std::string;
using string_iterator = std::string::iterator;

// prelim_dep: A preliminary (unresolved) service dependency
class prelim_dep
{
    public:
    std::string name;
    dependency_type dep_type;

    prelim_dep(const std::string &name_p, dependency_type dep_type_p)
        : name(name_p), dep_type(dep_type_p) { }
    prelim_dep(std::string &&name_p, dependency_type dep_type_p)
        : name(std::move(name_p)), dep_type(dep_type_p) { }
};

class service_record
{
public:
    service_record(std::string name, std::list<prelim_dep> dependencies_p) : dependencies(dependencies_p) {}

    std::string name;
    bool finished_loading = false;  // flag used to detect cyclic dependencies
    std::list<prelim_dep> dependencies;
};

using service_set_t = std::map<std::string, service_record *>;

service_record *load_service(service_set_t &services, const std::string &name,
        const service_dir_pathlist &service_dirs);

// Add some missing standard library functionality...
template <typename T> bool contains(std::vector<T> vec, const T& elem)
{
    return std::find(vec.begin(), vec.end(), elem) != vec.end();
}

int main(int argc, char **argv)
{
    using namespace std;

    service_dir_opt service_dir_opts;
    bool am_system_init = (getuid() == 0);

    std::vector<std::string> services_to_check;

    // Process command line
    if (argc > 1) {
        for (int i = 1; i < argc; i++) {
            if (argv[i][0] == '-') {
                // An option...
                if (strcmp(argv[i], "--services-dir") == 0 || strcmp(argv[i], "-d") == 0) {
                    if (++i < argc) {
                        service_dir_opts.set_specified_service_dir(argv[i]);
                    }
                    else {
                        cerr << "dinitcheck: '--services-dir' (-d) requires an argument" << endl;
                        return 1;
                    }
                }
                else if (strcmp(argv[i], "--help")) {
                    cout << "dinitcheck: check dinit service descriptions\n"
                            " --help                       display help\n"
                            " --services-dir <dir>, -d <dir>\n"
                            "                              set base directory for service description\n"
                            "                              files\n"
                            " <service-name>               check service with name <service-name>\n";
                    return EXIT_SUCCESS;
                }
                else {
                    std::cerr << "dinitcheck: Unrecognized option: '" << argv[i] << "' (use '--help' for help)\n";
                    return EXIT_FAILURE;
                }
            }
            else {
                services_to_check.push_back(argv[i]);
            }
        }
    }

    service_dir_opts.build_paths(am_system_init);

    if (services_to_check.empty()) {
        services_to_check.push_back("boot");
    }

    // Load named service(s)
    // - load the service, store dependencies as strings
    // - recurse

    std::map<std::string, service_record *> service_set;

    for (size_t i = 0; i < services_to_check.size(); ++i) {
        const std::string &name = services_to_check[i];
        std::cout << "Checking service: " << name << "...\n";
        try {
            service_record *sr = load_service(service_set, name, service_dir_opts.get_paths());
            service_set[name] = sr;
            // add dependencies to services_to_check
            for (auto &dep : sr->dependencies) {
                if (service_set.count(dep.name) == 0 && !contains(services_to_check, dep.name)) {
                    services_to_check.push_back(dep.name);
                }
            }
        }
        catch (service_load_exc &exc) {
            std::cerr << "Unable to load service '" << name << "': " << exc.exc_description << "\n";
        }
    }

    // TODO check for circular dependencies
    // TODO additional: check chain-to, other lint

    return 0;
}

static void report_service_description_exc(service_description_exc &exc)
{
    std::cerr << "Service '" << exc.service_name << "': " << exc.exc_description << "\n";
}

static void report_error(std::system_error &exc, const std::string &service_name)
{
    std::cerr << "Service '" << service_name << "', error reading service description: " << exc.what() << "\n";
}

{'fraction_non_alphanumeric': 0.1359393872845064, 'fraction_numerical': 0.0023059185242121443, 'mean_word_length': 2.27979834353619, 'pattern_counts': {'":': 0, '<': 143, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6465597', 'n_tokens_mistral': 2815, 'n_tokens_neox': 2720, 'n_words': 644}