@@ -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); }