mruby: add method KernAux.cmdline

This commit is contained in:
Alex Kotov 2022-02-07 18:46:25 +05:00
parent eadae265cc
commit 507eea0de2
Signed by: kotovalexarian
GPG Key ID: 553C0EBBEB5D5F08
4 changed files with 143 additions and 0 deletions

71
pkgs/mruby/src/cmdline.c Normal file
View File

@ -0,0 +1,71 @@
#include "main.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <kernaux.h>
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/presym.h>
#include <mruby/string.h>
#define ARGV_COUNT_MAX 256
#define BUFFER_SIZE 4096
static mrb_value rb_KernAux_cmdline(mrb_state *mrb, mrb_value self);
void init_cmdline(mrb_state *const mrb)
{
struct RClass *const rb_KernAux = mrb_module_get_id(mrb, MRB_SYM(KernAux));
struct RClass *const rb_KernAux_Error =
mrb_class_get_under_id(mrb, rb_KernAux, MRB_SYM(Error));
mrb_define_class_under_id(mrb, rb_KernAux, MRB_SYM(CmdlineError),
rb_KernAux_Error);
mrb_define_class_method(mrb, rb_KernAux, "cmdline",
rb_KernAux_cmdline, MRB_ARGS_REQ(1));
}
mrb_value rb_KernAux_cmdline(mrb_state *const mrb, mrb_value self)
{
const char *str;
mrb_get_args(mrb, "z", &str);
size_t argc;
char error_msg[KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX];
char **const argv = malloc(sizeof(char*) * ARGV_COUNT_MAX);
char *const buffer = malloc(BUFFER_SIZE);
const bool status = kernaux_cmdline(
str,
error_msg,
&argc,
argv,
buffer,
ARGV_COUNT_MAX,
BUFFER_SIZE
);
if (!status) {
free(argv);
free(buffer);
struct RClass *const rb_KernAux =
mrb_module_get_id(mrb, MRB_SYM(KernAux));
struct RClass *const rb_KernAux_CmdlineError =
mrb_class_get_under_id(mrb, rb_KernAux, MRB_SYM(CmdlineError));
mrb_raise(mrb, rb_KernAux_CmdlineError, error_msg);
}
mrb_value values[argc];
for (size_t index = 0; index < argc; ++index) {
values[index] = mrb_obj_freeze(
mrb,
mrb_str_cat_cstr(mrb, mrb_str_new_lit(mrb, ""), argv[index])
);
}
free(argv);
free(buffer);
return mrb_obj_freeze(mrb, mrb_ary_new_from_values(mrb, argc, values));
}

View File

@ -26,6 +26,7 @@ void mrb_mruby_kernaux_gem_init(mrb_state *const mrb)
init_assert(mrb);
init_ntoa(mrb);
init_cmdline(mrb);
}
void current_mrb_start(mrb_state *mrb)

View File

@ -9,5 +9,6 @@ mrb_state *current_mrb_get();
void init_assert(mrb_state *mrb);
void init_ntoa(mrb_state *mrb);
void init_cmdline(mrb_state *mrb);
#endif

View File

@ -0,0 +1,70 @@
def test_cmdline(str, expected)
result = KernAux.cmdline str
assert_true result.instance_of? Array
assert_true result.frozen?
result.each do |item|
assert_true item.instance_of? String
assert_true item.frozen?
end
assert_equal result, expected
end
assert 'default' do
test_cmdline 'foo bar\\ baz "car cdr"', ['foo', 'bar baz', 'car cdr']
end
assert 'when str is empty' do
test_cmdline '', []
end
assert 'when str has invalid type' do
assert_raise TypeError, 'Integer cannot be converted to String' do
KernAux.cmdline 123
end
end
assert 'when str has EOL after backslash' do
assert_raise KernAux::CmdlineError, 'EOL after backslash' do
KernAux.cmdline '\\'
end
end
assert 'when str has EOL after backslash inside quote' do
assert_raise KernAux::CmdlineError, 'EOL after backslash inside quote' do
KernAux.cmdline '"\\'
end
end
assert 'when str has unescaped quotation mark' do
assert_raise KernAux::CmdlineError, 'unescaped quotation mark' do
KernAux.cmdline 'foo"'
end
end
assert 'when str has EOL inside quote' do
assert_raise KernAux::CmdlineError, 'EOL inside quote' do
KernAux.cmdline '"'
end
end
assert 'when there are not too many args' do
test_cmdline 'a ' * 256, ['a'] * 256
end
assert 'when there are too many args' do
assert_raise KernAux::CmdlineError, 'too many args' do
KernAux.cmdline 'a ' * 257
end
end
assert 'when args don\'t cause buffer overflow' do
test_cmdline 'a' * 4095, ['a' * 4095]
end
assert 'when args cause buffer overflow' do
assert_raise KernAux::CmdlineError, 'buffer overflow' do
KernAux.cmdline 'a' * 4096
end
end