Workshop o mikrokontrolérech na SKSP 2024.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
3.9 KiB

3 months ago
/*
* UCW Library -- Formatting of command-line option help
*
* (c) 2013 Maria Matejka <mq@ucw.cz>
* (c) 2014 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
*/
#include <ucw/lib.h>
#include <ucw/opt.h>
#include <ucw/opt-internal.h>
#include <ucw/mempool.h>
#include <ucw/gary.h>
#include <string.h>
struct help {
struct mempool *pool;
struct help_line *lines; // A growing array of lines
};
struct help_line {
const char *extra;
char *fields[3];
};
static void opt_help_scan_item(struct help *h, struct opt_precomputed *opt)
{
const struct opt_item *item = opt->item;
if (!item->help)
return;
bool force_col1 = 0;
if (item->cls == OPT_CL_HELP) {
if (item->flags & OPT_HELP_COL) {
force_col1 = 1;
} else {
struct help_line *l = GARY_PUSH(h->lines);
l->extra = item->help;
return;
}
}
if (item->letter >= OPT_POSITIONAL_TAIL)
return;
struct help_line *first = GARY_PUSH(h->lines);
char *text = mp_strdup(h->pool, item->help);
struct help_line *l = first;
while (text) {
char *eol = strchr(text, '\n');
if (eol)
*eol++ = 0;
int field = (l == first && !force_col1 ? 1 : 0);
char *f = text;
while (f) {
char *tab = strchr(f, '\t');
if (tab)
*tab++ = 0;
if (field < 3)
l->fields[field++] = f;
f = tab;
}
text = eol;
if (text)
l = GARY_PUSH(h->lines);
}
if (item->name) {
char *val = first->fields[1] ? : "";
if (opt->flags & OPT_REQUIRED_VALUE)
val = mp_printf(h->pool, "=%s", val);
else if (!(opt->flags & OPT_NO_VALUE))
val = mp_printf(h->pool, "[=%s]", val);
first->fields[1] = mp_printf(h->pool, "--%s%s", item->name, val);
}
if (item->letter) {
if (item->name)
first->fields[0] = mp_printf(h->pool, "-%c, ", item->letter);
else {
char *val = first->fields[1] ? : "";
if (!(opt->flags & OPT_REQUIRED_VALUE) && !(opt->flags & OPT_NO_VALUE))
val = mp_printf(h->pool, "[%s]", val);
first->fields[0] = mp_printf(h->pool, "-%c%s", item->letter, val);
first->fields[1] = NULL;
}
}
}
static void opt_help_scan(struct help *h, const struct opt_section *sec)
{
for (const struct opt_item * item = sec->opt; item->cls != OPT_CL_END; item++) {
if (item->cls == OPT_CL_SECTION)
opt_help_scan(h, item->u.section);
else if (item->cls == OPT_CL_HOOK)
;
else {
struct opt_precomputed opt;
opt_precompute(&opt, item);
opt_help_scan_item(h, &opt);
}
}
}
void opt_help(const struct opt_section * sec) {
// Prepare help text
struct help h;
h.pool = mp_new(4096);
GARY_INIT_ZERO(h.lines, 0);
opt_help_scan(&h, sec);
// Calculate natural width of each column
uint n = GARY_SIZE(h.lines);
uint widths[3] = { 0, 0, 0 };
for (uint i=0; i<n; i++) {
struct help_line *l = &h.lines[i];
for (uint f=0; f<3; f++) {
if (!l->fields[f])
l->fields[f] = "";
uint w = strlen(l->fields[f]);
widths[f] = MAX(widths[f], w);
}
}
if (widths[0] > 4) {
/*
* This is tricky: if there are short options, which have an argument,
* but no long variant, we are willing to let column 0 overflow to column 1.
*/
widths[1] = MAX(widths[1], widths[0] - 4);
widths[0] = 4;
}
widths[1] += 4;
// Print columns
for (uint i=0; i<n; i++) {
struct help_line *l = &h.lines[i];
if (l->extra)
puts(l->extra);
else {
int t = 0;
for (uint f=0; f<3; f++) {
t += widths[f];
t -= printf("%s", l->fields[f]);
while (t > 0) {
putchar(' ');
t--;
}
}
putchar('\n');
}
}
// Clean up
GARY_FREE(h.lines);
mp_delete(h.pool);
}
void opt_handle_help(const struct opt_item * opt UNUSED, const char * value UNUSED, void * data)
{
struct opt_context *oc = data;
opt_help(oc->options);
exit(0);
}