spz-lab4/src/cli.c

251 lines
4.9 KiB
C
Raw Normal View History

2025-04-23 22:51:00 +03:00
#include "macro.h"
#include "config.h"
#include "cli.h"
#include "print.h"
#include "fs.h"
#include "ctrl.h"
#include <stdlib.h>
#include <string.h>
static const struct CliCommandEntry cmd[] = {
// mandatory commands
{"mkfs", 1, (enum CliArgType[]) {INT}, fs_mkfs},
{"create", 1, (enum CliArgType[]) {STR}, fs_create},
// custom commands
{"use", 1, (enum CliArgType[]) {STR}, fs_use},
{"prohibit-write", 0, NULL, fs_prohibit_write},
{"allow-write", 0, NULL, fs_allow_write},
{"exit", 0, NULL, ctrl_exit}
};
static void destroy_tokenized_line(char **tokenized_line)
{
for (int j = 0; j < CLI_MAX_ACCEPTED_ARGS; j++)
if (tokenized_line[j])
free(tokenized_line[j]);
free(tokenized_line);
}
static int tokenized_line_len(char **t)
{
int i;
for (i = 0; i < CLI_MAX_ACCEPTED_ARGS; i++)
if (!t[i])
break;
return i;
}
static char **tokenize_line(char *line, ssize_t result)
{
if (result > CLI_MAX_LINE_LENGTH) {
pr_err("command too long (%d > %d)\n", result+1, CLI_MAX_LINE_LENGTH);
return NULL;
}
char **tokenized_line = malloc(sizeof(char *) * CLI_MAX_ACCEPTED_ARGS);
memset(tokenized_line, 0, sizeof(char *) * CLI_MAX_ACCEPTED_ARGS);
unsigned int i, t, l;
for (
i = 0, t = 0, l = 0;
i < CLI_MAX_LINE_LENGTH,
t <= CLI_MAX_ACCEPTED_ARGS,
l < CLI_MAX_TOKEN_LENGTH;
i++
) {
if (line[i] == '\n') {
if (l) {
if (t == CLI_MAX_ACCEPTED_ARGS) {
t++;
break;
}
tokenized_line[t] = malloc(sizeof(char) * (l+1));
memcpy(tokenized_line[t], &(line[i-l]), l);
tokenized_line[t][l] = '\0';
t++;
}
break;
} else if (line[i] == ' ') {
if (l) {
if (t == CLI_MAX_ACCEPTED_ARGS) {
t++;
break;
}
tokenized_line[t] = malloc(sizeof(char) * (l+1));
memcpy(tokenized_line[t], &(line[i-l]), l);
tokenized_line[t][l] = '\0';
l = 0;
t++;
}
} else {
l++;
}
}
if (
i >= CLI_MAX_LINE_LENGTH ||
t > CLI_MAX_ACCEPTED_ARGS ||
l >= CLI_MAX_TOKEN_LENGTH ||
t == 0
) goto cli_process_error;
return tokenized_line;
cli_process_error:
// print error
if (i >= CLI_MAX_LINE_LENGTH)
pr_err("command too long (> %d)\n", CLI_MAX_LINE_LENGTH);
if (t > CLI_MAX_ACCEPTED_ARGS)
pr_err("too many arguments (> %d)\n", CLI_MAX_ACCEPTED_ARGS);
if (l >= CLI_MAX_TOKEN_LENGTH)
pr_err("argument %d too long (> %d)\n", t, CLI_MAX_TOKEN_LENGTH);
// clean up
destroy_tokenized_line(tokenized_line);
// return
return NULL;
}
void *format_args(char **tokenized_line, unsigned int arg_count, enum CliArgType *arg_types)
{
char *new_struct;
{
// prepare struct space
unsigned int struct_size = 0;
for (unsigned int i = 0; i < arg_count; i++)
switch (arg_types[i]) {
case INT:
struct_size += sizeof(int);
break;
case STR:
struct_size += sizeof(char *);
break;
}
new_struct = malloc(struct_size);
memset(new_struct, 0, struct_size);
}
unsigned int struct_offset = 0;
for (unsigned int i = 0; i < arg_count; i++)
{
if (arg_types[i] == INT) {
if (!tokenized_line[i+1]) {
pr_err("missing argument %d of type 'integer'\n", i+1);
goto abandon_struct;
}
int value;
int result = sscanf(tokenized_line[i+1], "%d", &value);
if (result != 1) {
pr_err("argument %d must be of type 'integer'\n", i+1);
goto abandon_struct;
}
*((int*) &(new_struct[struct_offset])) = value;
struct_offset += sizeof(int);
}
else if (arg_types[i] == STR) {
if (!tokenized_line[i+1]) {
pr_err("missing argument %d of type 'string'\n", i+1);
goto abandon_struct;
}
*((char**) &(new_struct[struct_offset])) = tokenized_line[i+1];
struct_offset += sizeof(char *);
}
}
return new_struct;
abandon_struct:
free(new_struct);
return NULL;
}
unsigned int cli_poll_process_next(void)
{
for ( ;; ) {
fputs("$ ", stdout);
size_t buf_size = 0;
char *line = NULL;
ssize_t result = getline(&line, &buf_size, stdin);
if (result == -1)
return 0x1;
char **tokenized_line = tokenize_line(line, result);
free(line);
if (tokenized_line == NULL)
continue;
unsigned int i;
for (i = 0; (i < LEN(cmd)) && strcmp(tokenized_line[0], cmd[i].cmd); i++);
if (i == LEN(cmd)) {
pr_err("command '%s' not found\n", tokenized_line[0]);
destroy_tokenized_line(tokenized_line);
continue;
}
{
int tll = tokenized_line_len(tokenized_line);
if ((cmd[i].arg_count + 1) != tll) {
pr_err("wrong amount of arguments for '%s' (%d required, %d got)\n", tokenized_line[0], cmd[i].arg_count, tll-1);
destroy_tokenized_line(tokenized_line);
continue;
}
}
int (*executor)(void *) = cmd[i].process;
void *data;
if (cmd[i].arg_count) {
data = format_args(tokenized_line, cmd[i].arg_count, cmd[i].args);
if (data == NULL) {
destroy_tokenized_line(tokenized_line);
continue;
}
} else {
data = NULL;
}
int execution_result_code = executor(data);
if (data)
free(data);
destroy_tokenized_line(tokenized_line);
if (0x1 == execution_result_code)
return 0x1;
}
}