Index: mod/kcli/cli.h ================================================================== --- mod/kcli/cli.h +++ mod/kcli/cli.h @@ -7,11 +7,11 @@ #include /* input types */ typedef struct kcli_opt { - codepoint id; + rune 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 */, @@ -48,13 +48,16 @@ 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, + kcli_rule_optional, + kcli_rule_overflow, } rule; + void* val; + const char* desc; } kcli_param; typedef struct kcli_set { const char* name; const char* version; @@ -70,10 +73,11 @@ 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_overlong /* a string in the configuration structures is longer than allowed */, } kcli_cond; typedef enum kcli_flag { kcli_flag_off = 0, kcli_flag_on = 1, Index: mod/kcli/kcli.md ================================================================== --- mod/kcli/kcli.md +++ mod/kcli/kcli.md @@ -86,11 +86,11 @@ } ## 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) + * `rune id` - the short single-character form of the flag as a UTF-8 codepoint (or initial 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) Index: mod/kcli/testbin.exe.c ================================================================== --- mod/kcli/testbin.exe.c +++ mod/kcli/testbin.exe.c @@ -1,14 +1,37 @@ #include #include stat entry (kenv e) { + kcli_flag verbose = false, + debug = false; + + kcli_opt options[] = { + { "v", "verbose", kcli_opt_flag, &verbose, + "induce information overload" }, + { "d", "debug-mode", kcli_opt_flag, &debug, + "run the program in debug mode"}, + }; + + const char* perp, * place, * weapon; + + kcli_param params[] = { + {"perp", kcli_param_string, kcli_rule_required, &perp, + "the one who did the dastardly deed"}, + {"place", kcli_param_string, kcli_rule_required, &place, + "where the crime was committed"}, + {"murder weapon", kcli_param_string, kcli_rule_optional, &weapon, + "the implement used to murder the victim"}, + }; + kcli_set testbin = { "testbin", "1.0.0", e.args, e.argc, "this is a test of the kcli module", + params, Kmsz(params), + options, Kmsz(options), }; kcond c = kcli_usage(testbin, e.err); return c; } Index: mod/kcli/usage.fn.c ================================================================== --- mod/kcli/usage.fn.c +++ mod/kcli/usage.fn.c @@ -1,20 +1,96 @@ +#include #include #include #include + +extern char* _k_internal_binary_name; + +static const struct { const char* front, * back; } param_styles [] = { + [kcli_rule_required] = { "<",">" }, + [kcli_rule_optional] = { "[","]" }, + [kcli_rule_overflow] = { "","..." }, +}; kcond kcli_usage(kcli_set prg, kiochan ch) { + enum { max_name_len = 32 }; + + kcond c; ubyte buf_space [sizeof(ksbuf) + 256]; ksbuf* out = ksbufmk(buf_space, ch, 256); + const char* name = prg.name != null ? + prg.name : _k_internal_binary_name; + const char* msg [] = { - prg.name, " v", prg.version, "\n\n", + name, " v", prg.version, "\n\n", prg.desc, "\n\n", + "usage: ", _k_internal_binary_name, + prg.optc == 0 ? null : " [-", }; - for (sz i = 0; i != Kmsz(msg); ++ i) { - ksraw str = { 0, msg[i] }; - kcond c = ksbufput(out, str); + + if (!kokay(c = ksbufwrite(out, msg))) return c; + + u8 longest_opt = 0; + u8 opt_lens [prg.optc]; + for (sz i = 0; i != prg.optc; ++ i) { + if (prg.opts[i].id[0] == 0) continue; + + opt_lens[i] = kssz(prg.opts[i].name, max_name_len); + if (opt_lens[i] > longest_opt) + longest_opt = opt_lens[i]; + + ksraw str = { kssz(prg.opts[i].id, 4), prg.opts[i].id }; + if (!kokay(c = ksbufput(out, str))) return c; } + + if (!kokay(c = ksbufput(out, Ksraw("]")))) return c; + u8 longest_param = 0; + u8 param_lens [prg.paramc]; + for (sz i = 0; i != prg.paramc; ++ i) { + enum kcli_rule r = prg.params[i].rule; + + param_lens[i] = kssz(prg.params[i].name, max_name_len); + if (param_lens[i] > longest_param) + longest_param = param_lens[i]; + + const char* param[] = { + " ", param_styles[r].front, prg.params[i].name, + param_styles[r].back, null + }; + + if (!kokay(c = ksbufwrite(out,param))) return c; + } + + if (!kokay(c = ksbufput(out, Ksraw("\n")))) return c; + + const char spacing [] = " "; + Kassert(sizeof spacing - 1 == max_name_len, + "spacing string not equal in length to max_name_len"); + /* yes, yes, i know */ + + for (sz i = 0; i != prg.paramc; ++ i) { + u8 pad = (longest_param - param_lens[i]) + 1; + const char* tpl [] = { + "\n ", spacing + (sizeof spacing - pad), + prg.params[i].name, ": ", prg.params[i].desc, null + }; + + if (!kokay(c = ksbufwrite(out, tpl))) return c; + } + + if (!kokay(c = ksbufput(out, (ksraw){1, "\n"}))) return c; + + for (sz i = 0; i != prg.optc; ++ i) { + u8 pad = (longest_opt - opt_lens[i]) + 1; + const char* tpl [] = { + "\n -", prg.opts[i].id, ", --", prg.opts[i].name, + spacing + (sizeof spacing - pad), ": ", prg.opts[i].desc, null + }; + + if (!kokay(c = ksbufwrite(out, tpl))) return c; + } + return ksbufflush(out); } Index: mod/kcore/boot.rt.c ================================================================== --- mod/kcore/boot.rt.c +++ mod/kcore/boot.rt.c @@ -1,13 +1,17 @@ #include #include extern stat entry(kenv); +const char* _k_internal_binary_name; + unsigned long long _boot(unsigned int argc, /* argument count */ const char** argv, /* arguments */ char** envp /* environment */ ) { + + _k_internal_binary_name = argv[0]; 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 the moment. */ ADDED mod/kstr/bufwrite.fn.c Index: mod/kstr/bufwrite.fn.c ================================================================== --- mod/kstr/bufwrite.fn.c +++ mod/kstr/bufwrite.fn.c @@ -0,0 +1,14 @@ +#include +#include +#include + +kcond +ksbufwrite(ksbuf* out, const char** array) { + for (sz i=0; array[i] != null; ++ i) { + ksraw str = { 0, array[i] }; + kcond c = ksbufput(out, str); + if (!kokay(c)) return c; + } + + return kscond_ok; +} ADDED mod/kstr/emitc.fn.c Index: mod/kstr/emitc.fn.c ================================================================== --- mod/kstr/emitc.fn.c +++ mod/kstr/emitc.fn.c @@ -0,0 +1,14 @@ +#include +#include +#include + +kcond +ksemitc(const char** array, sz bufsz, kiochan channel) { + ubyte cache [sizeof(ksbuf) + bufsz]; + ksbuf* out = ksbufmk(cache, channel, bufsz); + + kcond c = ksbufwrite(out, array); + if (!kokay(c)) return c; + + return ksbufflush(out); +} Index: mod/kstr/kstr.md ================================================================== --- mod/kstr/kstr.md +++ mod/kstr/kstr.md @@ -57,11 +57,11 @@ `ksbuf* ksbufmk(void* where, kiochan channel, sz run)` initializes a new buffer at the specified address. `run` should be equivalent to the full length of the memory region minus `sizeof(struct ksbuf)` - in other words, the size of the string the `ksbuf` can hold. memory should be allocated by the user, either by creating an array on the stack or with `kmem` allocation functions. `ksbufmk()` returns a pointer to the new structure. the return value will always point to the same point in memory as `where`, but will be of the appropriate type to pass to buffer functions. ## ksbufput `kcond ksbufput(ksbuf* b, ksraw str)` copies a string into a buffer with `kscp`. flushing it as necessary. -# ksbufflush +## ksbufflush `kcond ksbufflush(ksbuf* b)` flushes a buffer to its assigned channel, emptying it and readying it for another write cycle. a buffer should almost always be flushed before it goes out of scope or is deallocated. ## kscomp `char* kscomp(size_t ct, ksraw struct[], kmbuf* buf)` is a **string composition** function. it serves as an efficient, generalized replacement for functions like `strcat` and `strdup`. @@ -78,9 +78,15 @@ }; char* html = kscomp(Kmsz(chain), chain, &buf); kscomp will only calculate the length of individual strings if they are not already known. when it needs to calculate the length of a string, it will store that length in the original array so repeated calls can be made without needing to repeatedly calculate the lengths. this is not always desirable, so the variant `kscompc` exists, which is exactly the same as `kscomp` in every respect except that `chain` is not altered in any way. -## macros +## ksemit +`ksemit(sz len, ksraw* array, kiochan channel)` takes a `len`-length `array` of `ksraw`s and prints them to a channel. a buffer will be allocated based on the total length of the strings to avoid unnecessary write calls. + +## ksemitc +`ksemitc(const char** array, sz bufsz, kiochan channel)` takes a null-terminated `array` of NUL-terminated strings and buffer-prints them to a channel. `bufsz` controls the size of the buffer used, and should be as close as possible to the size of the strings emitted. the buffer will be kept on the stack, so no memory management or cleanup is necessary. + +# macros if `KFclean` is not set when is included, the following macros are defined. - * `Kstr(string)` - the compile-time equivalent to `kstr()`. `Kstr` takes a literal string and inserts the text `{ sizeof (string), string }` into the document, suitable for initializing a kstr. + * `Kstr(string)` - the compile-time equivalent to `kstr()`. `Kstr` takes a literal string and inserts the text `{ sizeof (string) - 1, string }` into the document, suitable for initializing a kstr. Index: mod/kstr/str.h ================================================================== --- mod/kstr/str.h +++ mod/kstr/str.h @@ -10,10 +10,12 @@ typedef struct ksraw { sz size; const char* ptr; } ksraw; +#define Ksraw(str) ((ksraw){sizeof(str),str}) + typedef struct ksmut { sz size; char* ptr; } ksmut; @@ -23,10 +25,11 @@ typedef struct kstr { sz size; kmptr ptr; } kstr; + #include #include typedef enum kscond { @@ -77,14 +80,15 @@ ksbuf* ksbufmk(void* where, kiochan channel, sz run); #include -kcond ksbufput(ksbuf*, ksraw); - +kcond ksbufput(ksbuf*, ksraw); kiocond ksbufflush(ksbuf*); +kcond ksbufwrite(ksbuf*, const char** array); +kcond ksemitc(const char** array, sz bufsz, kiochan channel); #ifdef __cplusplus } #endif #endif