Program Listing for File server_args_parser.c

Return to documentation for file (server/server_args_parser.c)

#include <getopt.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "bits/getopt_core.h"
#include "client/client.h"
#include "utils/debug.h"

#include "server_args_parser.h"

// Helper message to avoid long strings in the code
static constexpr const char INVALID_ARG[] = {
    "Invalid option or missing argument\n%s\n"
};

// Structure to hold the command line parameters, to be used by getopt_long
static const struct option long_options[] = {
    {"help", no_argument, nullptr, 'h'},
    {"port", required_argument, nullptr, 'p'},
    {"width", required_argument, nullptr, 'x'},
    {"height", required_argument, nullptr, 'y'},
    {"names", required_argument, nullptr, 'n'},
    {"client-number", required_argument, nullptr, 'c'},
    {"freq", required_argument, nullptr, 'f'},
    {nullptr, 0, nullptr, 0}
};

static
size_t get_team_slot(char **teams, const char *team_name, size_t count)
{
    size_t i = 0;

    for (; i < count; i++)
        if (strcmp(teams[i], team_name) == 0)
            return i;
    return count;
}

static
bool parse_teams(params_t *params, char *argv[], int *idx)
{
    size_t i = 0;
    size_t slot;

    if (argv[*idx] == nullptr)
        return false;
    for (; argv[*idx + i] != nullptr && *argv[*idx + i] != '-'; i++);
    for (size_t j = 0; j < i; j++) {
        slot = get_team_slot(
            params->teams, argv[*idx + j], params->registered_team_count);
        if (slot != params->registered_team_count)
            return fprintf(stderr, (slot > TEAM_ID_GRAPHIC)
                ? "Team '%s' is already registered.\n"
                : "Cannot create reserved team name '%s'\n"
                , argv[*idx + j]), false;
        params->teams[params->registered_team_count] = argv[*idx + j];
        params->registered_team_count++;
    }
    *idx += i;
    params->teams[params->registered_team_count] = nullptr;
    return true;
}

static
uint16_t parse_number_arg(
    const char *arg, const char *name, uint16_t min, uint16_t max)
{
    char *endptr;
    long value = strtol(arg, &endptr, 10);

    if (*endptr != '\0' || value < min || value > max) {
        fprintf(stderr,
            "Invalid value for %s: %s (must be between %u and %u)\n",
            name, arg, min, max
        );
        return 0;
    }
    return value;
}

static
bool number_arg_dispatcher(params_t *params, const char *arg, char opt)
{
    switch (opt) {
        case 'f':
            params->frequency = parse_number_arg(arg, "f", 1, 10000);
            break;
        case 'x':
            params->map_width = parse_number_arg(arg, "x", 10, 42);
            break;
        case 'y':
            params->map_height = parse_number_arg(arg, "y", 10, 42);
            break;
        case 'p':
            params->port = parse_number_arg(arg, "p", 1024, 65535);
            break;
        case 'c':
            params->team_capacity = parse_number_arg(arg, "c", 1, 200);
            break;
        default:
            return fprintf(stderr, INVALID_ARG, SERVER_USAGE), false;
    }
    return true;
}

static
bool arg_dispatcher(params_t *params, char *argv[], char opt)
{
    switch (opt) {
        case 'h':
            params->help = true;
            return true;
        case 'n':
            optind--;
            if (!parse_teams(params, argv, &optind)) {
                fprintf(stderr, "Failed to parse team names.\n");
                return false;
            }
            return true;
        case '?':
        default:
            return number_arg_dispatcher(params, optarg, opt);
    }
}

DEBUG_USED static
void print_params(const params_t *params)
{
    DEBUG_MSG("===================Zappy Server===================");
    DEBUG("port = %d", params->port);
    DEBUG("width = %d", params->map_width);
    DEBUG("heigth = %d", params->map_height);
    DEBUG("clients_nb = %d", params->team_capacity);
    DEBUG("freq = %d", params->frequency);
    DEBUG_MSG("Teams:");
    for (size_t i = 0; params->teams[i] != nullptr; i++)
        DEBUG("  - %s: id = [%03zu]", params->teams[i], i);
    DEBUG_MSG("==================================================");
}

bool parse_args(params_t *params, int argc, char *argv[])
{
    for (int opt;;) {
        opt = getopt_long(argc, argv, "hp:x:y:n:c:f:", long_options, nullptr);
        if (opt < 0)
            break;
        if (!arg_dispatcher(params, argv, opt))
            return false;
    }
    if (params->frequency == 0)
        params->frequency = 100;
    if (params->port == 0
        || params->map_width == 0
        || params->map_height == 0
        || params->team_capacity == 0
        || params->teams == nullptr
    )
        return fprintf(stderr, "%s", SERVER_USAGE), false;
    DEBUG_CALL(print_params, params);
    return true;
}