Index: build.sh ================================================================== --- build.sh +++ build.sh @@ -23,11 +23,11 @@ ./clean.sh fi # TODO: make it possible for user to change # default set with environment vars -modules=(kcore kmem kstr kio kgraft kfile) +modules=(kcore kmem kstr kio kgraft kfile kcli) # compose an arch tuple. this is used in # places, mostly to select the correct # version of assembly files for a given # platform (each has an arch tuple in its Index: global/build-manpage.sh ================================================================== --- global/build-manpage.sh +++ global/build-manpage.sh @@ -44,15 +44,15 @@ else descline=$(grep -m2 -n "^#" "$file" | tail -n1 | cut -d: -f1) offset=0 fi -tail -n+2 $file | head -n$(expr $descline - 2) | cmark -t man >>"$fmt" +tail -n+2 $file | head -n$(expr $descline - 2) | cmark --smart -t man >>"$fmt" echo >>"$fmt" ".SH DESCRIPTION" -tail -n+$(expr $descline + $offset) "$file" | cmark -t man >> "$fmt" +tail -n+$(expr $descline + $offset) "$file" | cmark --smart --normalize -t man >> "$fmt" test "$doc_html" = "yes" && { mkdir -p "$htmldest" groff -Thtml -Kutf8 -m man "$fmt" > "$html" test "$verbose" != "loud" || ADDED mod/kcli/cli.h Index: mod/kcli/cli.h ================================================================== --- mod/kcli/cli.h +++ mod/kcli/cli.h @@ -0,0 +1,94 @@ +#ifndef KIcli +#define KIcli + +#include +#include +#include +#include + +/* input types */ + +typedef struct kcli_opt { + codepoint id; + const char* name; + enum kcli_opt_kind { + kcli_opt_none /* flag is disabled and hidden */, + kcli_opt_string /* flag takes a string argument */, + kcli_opt_flag /* on if present, off if absent */, + kcli_opt_toggle /* off by default, value toggles + * for each occurrence */, + kcli_opt_accumulate /* u8, value increases for each + * occurrence, saturates at 255 */, + kcli_opt_enum /* matched against a table of string + * values to retrieve the corresponding + * integer ID */, + + kcli_opt_int = 0x40, + /* kcli_opt_int takes a default base parameter. + * for instance, to take a decimal number, you + * might write `kcli_opt_int | 10.` if the + * argument takes a base prefix, the default + * base will be disregarded. */ + kcli_opt_oct = kcli_opt_int | 8, + kcli_opt_dec = kcli_opt_int | 10, + kcli_opt_hex = kcli_opt_int | 16, + } kind; + void* val; + const char* desc; +} kcli_opt; + +typedef struct kcli_param { + const char* name; + enum kcli_param_kind { + kcli_param_none, + kcli_param_string, + kcli_param_enum, + kcli_param_int = 0x40, + kcli_param_oct = kcli_param_int | 8, + kcli_param_dec = kcli_param_int | 10, + kcli_param_hex = kcli_param_int | 16, + } kind; + enum kcli_rule { + kcli_rule_forbidden, + kcli_rule_optional, + kcli_rule_required, + } rule; +} kcli_param; + +typedef struct kcli_set { + const char* name; + const char* version; + const char** args; sz argc; + const char* desc; + const kcli_param* params; sz paramc; + const kcli_opt* opts; sz optc; +} kcli_set; + +/* output types */ + +typedef enum kcli_cond { + kcli_cond_ok = kcli_cond_id, + kcli_cond_extra /* parse succeded, but arguments or + flags were left over */, + kcli_cond_fail /* unspecified error */, + kcli_cond_parse /* bad syntax, parse failed */, +} kcli_cond; + +typedef enum kcli_flag { + kcli_flag_off = 0, + kcli_flag_on = 1, +} kcli_flag; + +/* functions */ + +kcond kcli_usage(kcli_set, kiochan); +kcli_cond kcli_parse(kcli_set); + +/* macros */ + +#define Kcli_param(field,kind,rule,description) \ + {#field, kcli_param_##kind, kcli_rule_##rule, &field, description} +#define Kcli_opt(long,short,kind,description) \ + {short, #long, kcli_opt_##kind, &long, description} + +#endif Index: mod/kcli/kcli.md ================================================================== --- mod/kcli/kcli.md +++ mod/kcli/kcli.md @@ -30,15 +30,16 @@ 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. +`kcli_set` is a struct containing information about your program, such as its name, a synopsis of its function, a pointer to an array of arguments, and a list of `kcli_opt`s. * `const char* name` - program name (if null, will be determined from argv instead) + * `const char* version` - a string describing the program version (if null, version will not be printed) + * `const char** args` - the `argv` pointer passed to the `entry` function, representing the command-line arguments passed to the program. * `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. @@ -53,15 +54,15 @@ kcli_flag zebra; char* user; char* password; long age; kcli_param params[] = { - { "user", kcli_param_string, kcli_class_required, + { "user", kcli_param_string, kcli_rule_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", kcli_param_dec, kcli_rule_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, @@ -71,79 +72,82 @@ "a striped equine" }, { 'p', "password", kcli_opt_string, &password, "the password to log in with" } }; kcli_set argset = { - "demo", e.argc, e.argv, + "demo", e.args, e.argc, "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; } + size_t args_parsed = kcli_parse(argset); + if (args_parsed == 0) { kcli_usage(me, e.err); return 1; } return 0; } -### struct kcli_opt +## 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 +## 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_int` - flag tells kcli to add an integer to the last of expected parameters; must be bitwise-OR'd with default base * `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 +## 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 + * `kcli_rule rule` - 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 +### 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_enum` - parameter will be matched against an enumeration table + * `kcli_param_int` - parameter will be parsed as an integer; must be bitwise-OR'd with default base * `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_rule + * `kcli_rule_forbidden` - parameter may not be passed + * `kcli_rule_optional` - parameter may or may not be passed + * `kcli_rule_required` - parameter must be passed -### enum kcli_flag +## 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 +# macros -### Kcli_param(field, kind, class, description) +## 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" } + { "field", kcli_param_a, kcli_rule_b, &field, "description" } -### Kcli_opt(field, kind, class, 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/kcli/parse.fn.c Index: mod/kcli/parse.fn.c ================================================================== --- mod/kcli/parse.fn.c +++ mod/kcli/parse.fn.c @@ -0,0 +1,6 @@ +#include + +kcli_cond +kcli_parse(kcli_set prg) { + +} ADDED mod/kcli/testbin.exe.c Index: mod/kcli/testbin.exe.c ================================================================== --- mod/kcli/testbin.exe.c +++ mod/kcli/testbin.exe.c @@ -0,0 +1,14 @@ +#include +#include + +stat +entry (kenv e) { + kcli_set testbin = { + "testbin", "1.0.0", e.args, e.argc, + "this is a test of the kcli module", + }; + + kcli_usage(testbin, e.err); + + return 0; +} ADDED mod/kcli/usage.fn.c Index: mod/kcli/usage.fn.c ================================================================== --- mod/kcli/usage.fn.c +++ mod/kcli/usage.fn.c @@ -0,0 +1,56 @@ +#include +#include +#include + +typedef struct buffer { + char* cur; + kiochan channel; + sz run; + char buf []; +} buffer; + +static buffer* +buffer_new(void* mem, kiochan channel, sz run) { + buffer* r = mem; + r -> cur = r -> buf; + r -> channel = channel; + r -> run = run; + return r; +} + +static kiocond +buffer_flush(buffer* b) { + ksraw str = {b -> cur - b -> buf, b -> buf}; + b -> cur = b -> buf; + return kiosend(b -> channel, str, null); +} + +static kcond +buffer_send(buffer* b, const char* str) { + ksmut buf = { b->run - (b->cur - b->buf), b->cur }; + ksraw src = { 0, str }; + kscond sc = kscp(src, buf, &src.size); + if (sc != kscond_ok) return sc; + + b->cur += src.size; + if (b->cur >= (b->buf + b->run)) { + return buffer_flush(b); + } else return kiocond_ok; +} + +kcond +kcli_usage(kcli_set prg, kiochan ch) { + ubyte buf_space [sizeof(buffer) + 256]; + buffer* out = buffer_new(buf_space, ch, 256); + + const char* msg [] = { + prg.name, " v", prg.version, "\n\n", + prg.desc, "\n\n", + }; + for (sz i = 0; i != sizeof msg / sizeof msg[0]; ++ i) { + kcond c = buffer_send(out, msg[i]); + if (!kokay(c)) return c; + } + + return buffer_flush(out); +} Index: mod/kcore/boot.rt.c ================================================================== --- mod/kcore/boot.rt.c +++ mod/kcore/boot.rt.c @@ -2,11 +2,11 @@ #include extern stat entry(kenv); unsigned long long _boot(unsigned int argc, /* argument count */ - char** argv, /* arguments */ + const char** argv, /* arguments */ char** envp /* environment */ ) { envp ++; /* envp seems to point at a leading null; this is probably a sign of breakage but i don't know what else to do about it for @@ -37,7 +37,8 @@ /* TODO: determine terminal class and set term vs ansi correctly! */ { {kiostream_term, 0}, {kiostream_term, 1} }, // chan std { {kiostream_closed}, {kiostream_term, 2} }, // chan err argc, argv, ep - envp, variables }; + return entry(e); } Index: mod/kcore/core.h ================================================================== --- mod/kcore/core.h +++ mod/kcore/core.h @@ -16,11 +16,11 @@ } kvar; typedef struct kenv { kiochan std; kiochan err; - sz argc; char** args; + sz argc; const char** args; sz varc; kvar* vars; } kenv; /* i'm really sorry okay */ typedef Index: mod/kcore/type.h.m ================================================================== --- mod/kcore/type.h.m +++ mod/kcore/type.h.m @@ -181,11 +181,11 @@ // exit status integer types - pls use kbad in instead ifelse(target_posix,“yes”,“ /* by convention, posix return values are 8-bit, * but note that many modern UNIXes do technically * support higher-bit values. for this reason, - * longstat is defined differently under posix. */ + * stat_long is defined differently under posix. */ typedef u8 stat; typedef u32 stat_long; ”,“dnl ifelse(atom_target_os,“win”,“ typedef u32 stat; @@ -199,6 +199,13 @@ * default. if this is wrong, help us fix it! */ ”)”)dnl typedef stat stat_long; ”)dnl +/* unicode types */ + +typedef u32 codepoint; + /* a codepoint is a UTF-32 character */ +typedef u8 rune [5]; + /* a rune is a valid UTF-8 character terminated with a nul. */ + #endif Index: mod/kio/io.h.m ================================================================== --- mod/kio/io.h.m +++ mod/kio/io.h.m @@ -76,10 +76,11 @@ kiocond_fail_too_big, kiocond_fail_stream_mismatch, } kiocond; kiocond kiosend(kiochan, ksraw, sz*); // send data to a channel +kiocond kiosendall(kiochan, ksraw); // keep sending data to a channel until it's all sent kiocond kiorecv(kiochan, ksraw*); // receive data from a channel kmptr kiorecvall(kiochan, kmcell*, kmkind); // automatically allocate a bufer for a channel // kmkind is only used if kmcell* is null kiocond kiocon(kiochan, kiochan); // connect one channel to another ADDED mod/kstr/scp.fn.c Index: mod/kstr/scp.fn.c ================================================================== --- mod/kstr/scp.fn.c +++ mod/kstr/scp.fn.c @@ -0,0 +1,30 @@ +#include +#include + +kscond kscp(ksraw str, ksmut dest, sz* len) { + if (str.size == 0) { /* calculate length on the fly */ + for(; str.ptr[str.size] != 0; ++ str.size) { + if (str.size >= dest.size) return kscond_no_room; + + dest.ptr[str.size] = str.ptr[str.size]; + } + } else { + if (dest.size < str.size) return kscond_no_room; + + if (dest.ptr == null || str.ptr == null) return kscond_null; + + for(sz idx = 0; idx < str.size; ++ idx) { + dest.ptr[idx] = str.ptr[idx]; + } + } + + /* did we copy a nul? */ + if (str.ptr[str.size - 1] != 0) + /* we didn't, can we? */ + if (dest.size > str.size) + /* set a final nul */ + dest.ptr[str.size] = 0; + + if (len != null) *len = str.size; + return kscond_ok; +} Index: mod/kstr/str.h ================================================================== --- mod/kstr/str.h +++ mod/kstr/str.h @@ -26,10 +26,12 @@ typedef enum kscond { kscond_ok = kscond_id, kscond_fail, kscond_unimplemented, kscond_nonnumeric, + kscond_no_room, + kscond_null, } kscond; enum ksconv { ksconv_default = 0, @@ -51,11 +53,13 @@ u8* dest, sz size); kscond ks_of_int(u8* number, sz size, enum ksconv mode, char* bufstart, sz bufsize); + +kscond kscp(ksraw str, ksmut dest, sz* len); #ifdef __cplusplus } #endif #endif