Blame packet/cmdparse.c

Packit b802ec
/*
Packit b802ec
    mtr  --  a network diagnostic tool
Packit b802ec
    Copyright (C) 2016  Matt Kimball
Packit b802ec
Packit b802ec
    This program is free software; you can redistribute it and/or modify
Packit b802ec
    it under the terms of the GNU General Public License version 2 as
Packit b802ec
    published by the Free Software Foundation.
Packit b802ec
Packit b802ec
    This program is distributed in the hope that it will be useful,
Packit b802ec
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b802ec
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit b802ec
    GNU General Public License for more details.
Packit b802ec
Packit b802ec
    You should have received a copy of the GNU General Public License
Packit b802ec
    along with this program; if not, write to the Free Software
Packit b802ec
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Packit b802ec
*/
Packit b802ec
Packit b802ec
#include "cmdparse.h"
Packit b802ec
Packit b802ec
#include <ctype.h>
Packit b802ec
#include <errno.h>
Packit b802ec
#include <stdlib.h>
Packit b802ec
#include <string.h>
Packit b802ec
Packit b802ec
/*
Packit b802ec
    NUL terminate the whitespace separated tokens in the command string.
Packit b802ec
    This modifies command_string in-place with NUL characters.
Packit b802ec
    Fill the tokens array with pointers to the tokens, and return the
Packit b802ec
    number of tokens found.
Packit b802ec
*/
Packit b802ec
static
Packit b802ec
int tokenize_command(
Packit b802ec
    char **tokens,
Packit b802ec
    int max_tokens,
Packit b802ec
    char *command_string)
Packit b802ec
{
Packit b802ec
    int token_count = 0;
Packit b802ec
    int on_space = 1;
Packit b802ec
    int i;
Packit b802ec
Packit b802ec
    for (i = 0; command_string[i]; i++) {
Packit b802ec
        if (on_space) {
Packit b802ec
            if (!isspace((unsigned char) command_string[i])) {
Packit b802ec
                /*  Take care not to exceed the token array length  */
Packit b802ec
                if (token_count >= max_tokens) {
Packit b802ec
                    return -1;
Packit b802ec
                }
Packit b802ec
Packit b802ec
                tokens[token_count++] = &command_string[i];
Packit b802ec
                on_space = 0;
Packit b802ec
            }
Packit b802ec
        } else {
Packit b802ec
            if (isspace((unsigned char) command_string[i])) {
Packit b802ec
                command_string[i] = 0;
Packit b802ec
                on_space = 1;
Packit b802ec
            }
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return token_count;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Parse a command string (or command reply string) into a command_t
Packit b802ec
    structure for later semantic interpretation.  Returns EINVAL if the
Packit b802ec
    command string is unparseable or zero for success.
Packit b802ec
Packit b802ec
    comamnd_string will be modified in-place with NUL characters terminating
Packit b802ec
    tokens, and the command_t will use pointers to the conents of
Packit b802ec
    command_string without copying, so any interpretation of the
Packit b802ec
    command_t structure requires that the command_string memory has not yet
Packit b802ec
    been freed or otherwise reused.
Packit b802ec
*/
Packit b802ec
int parse_command(
Packit b802ec
    struct command_t *command,
Packit b802ec
    char *command_string)
Packit b802ec
{
Packit b802ec
    char *tokens[MAX_COMMAND_TOKENS];
Packit b802ec
    int token_count;
Packit b802ec
    int i;
Packit b802ec
Packit b802ec
    memset(command, 0, sizeof(struct command_t));
Packit b802ec
Packit b802ec
    /*  Tokenize the string using whitespace  */
Packit b802ec
    token_count =
Packit b802ec
        tokenize_command(tokens, MAX_COMMAND_TOKENS, command_string);
Packit b802ec
    if (token_count < 2) {
Packit b802ec
        errno = EINVAL;
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    /*  Expect the command token to be a numerical value  */
Packit b802ec
    errno = 0;
Packit b802ec
    command->token = strtol(tokens[0], NULL, 10);
Packit b802ec
    if (errno) {
Packit b802ec
        errno = EINVAL;
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
    command->command_name = tokens[1];
Packit b802ec
Packit b802ec
    /*
Packit b802ec
       The tokens beyond the command name are expected to be in
Packit b802ec
       name, value pairs.
Packit b802ec
     */
Packit b802ec
    i = 2;
Packit b802ec
    command->argument_count = 0;
Packit b802ec
    while (i < token_count) {
Packit b802ec
        /*  It's an error if we get a name without a key  */
Packit b802ec
        if (i + 1 >= token_count) {
Packit b802ec
            errno = EINVAL;
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
Packit b802ec
        /*  It's an error if we get more arguments than we have space for  */
Packit b802ec
        if (command->argument_count >= MAX_COMMAND_ARGUMENTS) {
Packit b802ec
            errno = EINVAL;
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
Packit b802ec
        command->argument_name[command->argument_count] = tokens[i];
Packit b802ec
        command->argument_value[command->argument_count] = tokens[i + 1];
Packit b802ec
        command->argument_count++;
Packit b802ec
Packit b802ec
        i += 2;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return 0;
Packit b802ec
}