ADDED mod/kcli/kcli.md Index: mod/kcli/kcli.md ================================================================== --- mod/kcli/kcli.md +++ mod/kcli/kcli.md @@ -0,0 +1,126 @@ +# kcli +**kcli** is a module that implements common routines used by command-line utilities, such as option parsing, usage display, and more. + +## functions + +### kcli_usage(kcli_set, kiochan) +kcli_usage() takes a `kcli_set` and prints a succinct usage summary to a [kiochan](../kio/kio.md). + +## types + +### struct kcli_set +`kcli_set` is a struct containing information about your program, such as its name, a synopsis of its function, a pointer to `argv`, and a list of `kcli_opt`s. + + * `const char* name` - program name (if null, will be determined from argv instead) + * `size_t argc` - the number of arguments in the `argv` array. + * `const char** argv` - the `argv` pointer passed to the `entry` function, representing the command-line arguments passed to the program. + * `const char* desc` - program description + * `const kcli_param* params` - a list of options expected by the program. + * `size_t paramc` - the number of params in the list to process. + * `const kcli_opt* opts` - a list of options expected by the program. + * `size_t optc` - the number of options in the list to process. + +a kcli_set might be used like so: + + #include + #include + #include + stat entry(kenv e) { + kcli_flag aardvark; + kcli_flag zebra; + char* user; + char* password; + long age; + kcli_param params[] = { + { "user", kcli_param_string, kcli_class_required, + &user, "the user to log in as" } + // or Kcli_param(user,string,required,"the user to log in as"), + + { "age", kcli_param_dec, kcli_class_optional, + &age, "the age of the user" } + // or Kcli_param(age,dec,optional,"the age of the user"), + }; + kcli_opt options[] = { + { 'a', "aardvark", kcli_opt_flag, &aardvark, + "a nocturnal burrowing mammal" }, + // or Kcli_opt(aardvark, 'a', flag, "a nocturnal burrowing mammal") + { 'z', "zebra", kcli_opt_flag, &zebra, + "a striped equine" }, + { 'p', "password", kcli_opt_string, &password, + "the password to log in with" } + }; + kcli_set argset = { + "demo", e.argc, e.argv, + "a demonstration of the kcli_set type", + params, Kmsz(params), + options, Kmsz(options) + }, + size_t args_parsed = kcli_parse(&argset); + if (args_parsed == 0) { kcli_usage(&me, e.err); return 1; } + + return 0; + } + +### struct kcli_opt +a `kcli_opt` is a representation of a command-line flag and its function. each option must have a unique `id` and/or a unique `name`. + + * `char id` - the short single-character form of the flag (or NUL for no short form) + * `const char* name` - the long string form of the flag (or NULL for no long form) + * `kcli_opt_kind kind` - enum that describes how the flag will function + * `void* val` - a pointer to an appropriate type to store the return value in. + * `const char* desc` - a description of the flag's purpose and function (or NULL for no description) + +#### enum kcli_opt_kind + + * `kcli_opt_none` - flag is disabled and will not be shown in usage + * `kcli_opt_string` - flag tells kcli to add a string to the list of expected parameters; appropriate string will be returned + * `kcli_opt_oct` - flag tells kcli to add an octal number to the list of expected parameters + * `kcli_opt_dec` - flag tells kcli to add a decimal number to the list of expected parameters + * `kcli_opt_hex` - flag tells kcli to add a hexdecimal number to the list of expected parameters + * `kcli_opt_flag` - flag is an option: will return `kcli_flag_on` if entered at least once, `kcli_flag_off` otherwise. + * `kcli_opt_toggle` - flag toggles value on and off: will return `kcli_flag_on` if entered an odd number of times, `kcli_flag_off` otherwise. + * `kcli_opt_accumulate` - flag increments a value every time it is entered; often used to implement `-v (--verbose)`-style options (e.g. `-vvvv` would return a value of `4`). + * `kcli_opt_enum` - flag is one of a series of enumerated values, which will be matched against a table to yield the associated integer. + +### struct kcli_param +`kcli_param` describes a parameter that may be passed to the program whether or not any flags are passed. + + * `const char* name` - a short name for the parameter + * `kcli_param_kind kind` - the kind of parameter passed + * `kcli_class class` - whether or not the parameter is optional + * `void* val` - a pointer to an appropriate type of variable to fill + * `const char* desc` - a description of the parameter's function + +#### enum kcli_param_kind + * `kcli_param_none` - parameter is disabled and will not be expected or accepted + * `kcli_param_string` - parameter will not be parsed; a raw string will be returned + * `kcli_param_oct` - parameter will be parsed as an octal number + * `kcli_param_dec` - parameter will be parsed as a decimal number + * `kcli_param_hex` - parameter will be parsed as a hexadecimal number + +### enum kcli_class + * `kcli_class_forbidden` - parameter may not be passed + * `kcli_class_optional` - parameter may or may not be passed + * `kcli_class_required` - parameter must be passed + +### enum kcli_flag +results that an option of kind `kcli_opt_flag` can return. + + * `kcli_flag_off = 0` - flag is off + * `kcli_flag_on = 1` - flag is on + +## macros + +### Kcli_param(field, kind, class, description) +a convenience macro for filling out parameter lists. + +`Kcli_param(field,a,b,"description")` is transformed into: + + { "field", kcli_param_a, kcli_class_b, &field, "description" } + +### Kcli_opt(field, kind, class, description) +a convenience macro for filling out option lists. + +`Kcli_opt(name,'n',string,"description")` is transformed into: + + { 'n', "name", kcli_opt_string, &name, "description" } ADDED mod/kcore/okay.fn.c Index: mod/kcore/okay.fn.c ================================================================== --- mod/kcore/okay.fn.c +++ mod/kcore/okay.fn.c @@ -0,0 +1,7 @@ +#include +#include +#include + +bool kokay(kcond val) { + return (val % kglobal_module_offset) == 0; +} ADDED mod/kcore/syscall.fn.x86.lin.32.s Index: mod/kcore/syscall.fn.x86.lin.32.s ================================================================== --- mod/kcore/syscall.fn.x86.lin.32.s +++ mod/kcore/syscall.fn.x86.lin.32.s @@ -0,0 +1,101 @@ +; kcore/syscall.fn.x86.lin.32.s +; ~ lexi hale +; +; this function performs a syscall and stores its +; results in the variables provided. this makes it +; possible to bypass the hideous errno mechanism +; altogether and access the error value of a +; syscall directly. invoke as: +; +; void k_platform_syscall_raw(s32* result, u32* errno, +; syscall, u8 valency, s32[] args) + +bits 32 +%include "../arch/posix/x86.lin.32.s" +%include "../arch/x86.cdecl.32.s" +; vim: ft=nasm + + +%define result_ptr edi +%define errno_ptr ebx +%define valency ecx +%define args esi +%define opcode eax + +%macro handle_arg 1 + push dword [args] + sub args, 4 + dec valency + jz .perform_call +%endmacro + +global k_platform_syscall_raw:function +k_platform_syscall_raw: + ; locals: rbx = s32* result + ; r12 = u32* errno + ; arg 0 = s32* result + ; arg 1 = errno ptr + ; arg 2 = syscall num + ; arg 3 = valency + ; arg 4 = args ptr + + ; store the locals in registers that + ; are guaranteed not to be clobbered, + ; saving us some cycles pushing to + ; and popping back from the stack + pop result_ptr + pop errno_ptr + pop edx ; opcode + + ; set up registers for the loop + pop valency + pop args ; ptr to args array + mov eax, 4 + mul valency + add args, eax ; adjust args pointer to point + ; at end of array, so it can be + ; trivially pushed in backwards + + ; this needs to go before the loop + ; or it'll get clobbered + mov opcode, edx ; syscall number + + ; automatically generate the code + ; needed to move the arguments into + ; their correct registers. see above + %assign i 0 + %rep 6 + handle_arg i + %assign i i+1 + %endrep + + ; valency >= 7. this is not valid, so + ; we set our return value to 0 and the + ; error number to its maximum value in + ; order to indicate that the syscall + ; was invalid + mov dword [result_ptr], -1 + mov dword [errno_ptr], -1 + ret + + ; we have a valency match - perform the + ; requested syscall already stored in eax + .perform_call: sys.call + + ; check if an error has occurred, and + ; jump to the error handling routine + ; if necessary + test sys.reg.ret, sys.reg.ret + js .error + + ; move our return values into place and + ; return to the caller (which should + ; always be k_platform_syscall, btw) + mov [result_ptr], sys.reg.ret + mov dword [errno_ptr], 0 + ret + + .error: neg sys.reg.ret + mov dword [result_ptr], -1 + mov [errno_ptr], sys.reg.ret + ret ADDED mod/kmem/lina.fn.c Index: mod/kmem/lina.fn.c ================================================================== --- mod/kmem/lina.fn.c +++ mod/kmem/lina.fn.c @@ -0,0 +1,31 @@ +/* lina.fn.c - kmlina() - linear allocate + * ~ lexi hale + * kmlina() allocates the requested amount of space on + * the heap and returns a pointer to it. it allocates + * this memory simply by growing the heap the proper + * length. */ + +#include +#include + +#ifdef KFenv_posix +# include +#endif + +kmres kmlina(sz size) { + k_platform_syscall_arg + top = (sz)kmlini(), + newbrk = top + size; + + struct k_platform_syscall_answer a = k_platform_syscall + (k_platform_syscall_brk, 1, &newbrk); + + kmres reply; + + if (a.error != 0) reply.cond = kmcond_no_room, + reply.raw = 0; + else reply.cond = kmcond_ok, + reply.raw = (void*)a.ret; + + return reply; +} ADDED mod/kmem/lini.fn.c Index: mod/kmem/lini.fn.c ================================================================== --- mod/kmem/lini.fn.c +++ mod/kmem/lini.fn.c @@ -0,0 +1,29 @@ +/* lini.fn.c - kmlini() - initiate a linear segment + * ~ lexi hale + * this function is the initiator function for the + * linear memory store. it simply returns the current + * heap position by invoking the brk syscall with the + * value `0`. (note that the brk syscall and the libc + * wrapper have different behavior.) */ + +#include +#include + +#ifdef KFenv_posix +# include +#endif + +/* returns void* instead of kmres because kmlini has + * no failure state */ +void* kmlini(void) { + k_platform_syscall_arg zero = 0; + /* brk adjusts the heap break then returns the new + * break. by passing zero, we can get retrieve the + * current heap break. */ + struct k_platform_syscall_answer a = k_platform_syscall + (k_platform_syscall_brk, 1, &zero); + + /* no point in checking for errors, since none can + * actually occur; just return the kernel's answer. */ + return (void*)a.ret; +}