From: Jan 'Moskyt' Matejka Date: Mon, 6 May 2013 10:14:59 +0000 (+0200) Subject: Opt: help is now displaying correctly X-Git-Tag: v5.99~25^2~53 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=aa7a1606e1e68c21a9de7d08b665b8e5baedd642;p=libucw.git Opt: help is now displaying correctly --- diff --git a/ucw/opt.c b/ucw/opt.c index ebae85e4..3bd51d32 100644 --- a/ucw/opt.c +++ b/ucw/opt.c @@ -19,77 +19,140 @@ static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2); static void opt_failure(const char * mesg, ...) { va_list args; va_start(args, mesg); - stk_vprintf(mesg, args); + vfprintf(stderr, mesg, args); + fprintf(stderr, "\n"); exit(OPT_EXIT_BAD_ARGS); va_end(args); } -struct opt_section * opt_section_root; +#define OPT_ADD_DEFAULT_ITEM_FLAGS(item, flags) \ + do { \ + if (!(flags & OPT_VALUE_FLAGS) && \ + (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER)) { \ + fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name); \ + ASSERT(0); \ + } \ + else if (!(flags & OPT_VALUE_FLAGS)) \ + flags |= opt_default_value_flags[item->cls]; \ + } while (0) +#define OPT_ITEM_FLAGS(item) ((item->flags & OPT_VALUE_FLAGS) ? item->flags : item->flags | opt_default_value_flags[item->cls]) + +const struct opt_section * opt_section_root; + +#define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1) -void opt_help_noexit_internal(struct opt_section * help) { - uns first_column = 0; +void opt_help_noexit_internal(const struct opt_section * help) { + int sections_cnt = 0; + int lines_cnt = 0; for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) { if (item->flags & OPT_NO_HELP) continue; - if (item->cls == OPT_CL_HELP && item->u.help2 == NULL) continue; - if (item->cls == OPT_CL_SECTION) continue; - - uns linelen = 0; - if (item->cls == OPT_CL_HELP) { // two-column help line - if (first_column < strlen(item->help)) - first_column = strlen(item->help); + if (item->cls == OPT_CL_SECTION) { + sections_cnt++; continue; } - - if (item->letter) { // will write sth like "-x, --exclusive" - linelen = strlen("-x, --") + strlen(item->name); - } - else { // will write sth like "--exclusive" - linelen = strlen("--") + strlen(item->name); - } - - ASSERT(item->flags & OPT_VALUE_FLAGS); - - if (item->flags & OPT_REQUIRED_VALUE) { - linelen += strlen("=value"); - } - else if (item->flags & OPT_MAYBE_VALUE) { - linelen += strlen("(=value)"); + if (!*(item->help)) { + lines_cnt++; + continue; } - - if (linelen > first_column) - first_column = linelen; + FOREACHLINE(item->help) + lines_cnt++; } - char * spaces = alloca(first_column + 1); - char * buf = alloca(first_column + 1); - for (uns i=0;iflags & OPT_REQUIRED_VALUE) ? "=value" : ((it->flags & OPT_NO_VALUE) ? "" : "(=value)")) for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) { if (item->flags & OPT_NO_HELP) continue; - + if (item->cls == OPT_CL_HELP) { - fprintf(stderr, "%s", item->help); - if (item->u.help2 == NULL) - fprintf(stderr, "\n"); - else - fprintf(stderr, "%s %s\n", spaces + strlen(item->help), item->u.help2); + if (!*(item->help)) { + line++; + continue; + } +#define SPLITLINES(text) do { \ + FOREACHLINE(text) { \ + int cell = 0; \ + for (const char * b = begin, * e = begin; (e < end) && (e = strchrnul(b, '\t')) && (e > end ? (e = end) : end); b = e+1) { \ + lines[line][cell] = b; \ + if (cell >= 2) \ + break; \ + else \ + if (*e == '\t' && linelengths[cell] < (e - b)) \ + linelengths[cell] = e-b; \ + cell++; \ + } \ + line++; \ + } } while (0) + SPLITLINES(item->help); + continue; } - else if (item->cls == OPT_CL_SECTION) { - opt_help_noexit_internal(item->u.section); + + if (item->cls == OPT_CL_SECTION) { + sections[s++] = (struct opt_sectlist) { .pos = line, .sect = item->u.section }; + continue; } - else if (item->letter) { - sprintf(buf, "-%c, --%s%s", item->letter, item->name, VAL(item)); - fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help); + + uns valoff = strchrnul(item->help, '\t') - item->help; + uns eol = strchrnul(item->help, '\n') - item->help; + if (valoff > eol) + valoff = eol; +#define VAL(it) ((OPT_ITEM_FLAGS(it) & OPT_REQUIRED_VALUE) ? stk_printf("=%.*s", valoff, item->help) : ((OPT_ITEM_FLAGS(it) & OPT_NO_VALUE) ? "" : stk_printf("(=%.*s)", valoff, item->help))) + if (item->name) { + lines[line][1] = stk_printf("--%s%s", item->name, VAL(item)); + if (linelengths[1] < (int) strlen(lines[line][1])) + linelengths[1] = strlen(lines[line][1]); + lines[line][0] = ""; + if (linelengths[0] < 0) + linelengths[0] = 0; } - else { - sprintf(buf, "--%s%s", item->name, VAL(item)); - fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help); + if (item->letter) { + lines[line][0] = stk_printf("-%c,", item->letter); + if (linelengths[0] < (int) strlen(lines[line][0])) + linelengths[0] = strlen(lines[line][0]); + } +#undef VAL + + if (eol > valoff) { + lines[line][2] = item->help + valoff + 1; + } + + line++; + + if (*(item->help + eol)) + SPLITLINES(item->help + eol + 1); + } +#undef SPLITLINES + + s = 0; +#define FIELD(k) linelengths[k], MIN(strchrnul(lines[i][k], '\t')-lines[i][k],strchrnul(lines[i][k], '\n')-lines[i][k]), lines[i][k] +#define LASTFIELD(k) MIN(strchrnul(lines[i][k], '\t')-lines[i][k],strchrnul(lines[i][k], '\n')-lines[i][k]), lines[i][k] + for (int i=0;iopts[i]->item->name, str, len)) { if (strlen(pre->opts[i]->item->name) == len) { if (pre->opts[i]->count++ && (pre->opts[i]->flags & OPT_SINGLE)) - opt_failure("Option %s appeared the second time.\n", pre->opts[i]->item->name); + opt_failure("Option %s appeared the second time.", pre->opts[i]->item->name); return pre->opts[i]->item; } if (candidate) - opt_failure("Ambiguous prefix %s: Found matching %s and %s.\n", str, candidate->name, pre->opts[i]->item->name); + opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->item->name); else candidate = pre->opts[i]->item; } @@ -129,7 +192,7 @@ static struct opt_item * opt_find_item_longopt(char * str, struct opt_precompute if (candidate) return candidate; - opt_failure("Invalid option %s.\n", str); + opt_failure("Invalid option %s.", str); } #define OPT_NAME (longopt ? stk_printf("--%s", item->name) : stk_printf("-%c", item->letter)) @@ -141,7 +204,7 @@ static void opt_parse_value(struct opt_item * item, char * value, int longopt) { else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false") || !strcasecmp(value, "0")) *((int *) item->ptr) = 0; else - opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.\n", OPT_NAME); + opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.", OPT_NAME); break; case OPT_CL_STATIC: { @@ -150,22 +213,22 @@ static void opt_parse_value(struct opt_item * item, char * value, int longopt) { case CT_INT: e = cf_parse_int(value, item->ptr); if (e) - opt_failure("Integer value parsing failed for argument %s: %s\n", OPT_NAME, e); + opt_failure("Integer value parsing failed for argument %s: %s", OPT_NAME, e); break; case CT_U64: e = cf_parse_u64(value, item->ptr); if (e) - opt_failure("Unsigned 64-bit value parsing failed for argument %s: %s\n", OPT_NAME, e); + opt_failure("Unsigned 64-bit value parsing failed for argument %s: %s", OPT_NAME, e); break; case CT_DOUBLE: e = cf_parse_double(value, item->ptr); if (e) - opt_failure("Double value parsing failed for argument %s: %s\n", OPT_NAME, e); + opt_failure("Double value parsing failed for argument %s: %s", OPT_NAME, e); break; case CT_IP: e = cf_parse_ip(value, item->ptr); if (e) - opt_failure("IP parsing failed for argument %s: %s\n", OPT_NAME, e); + opt_failure("IP parsing failed for argument %s: %s", OPT_NAME, e); break; case CT_STRING: item->ptr = strdup(value); @@ -194,7 +257,7 @@ static void opt_parse_value(struct opt_item * item, char * value, int longopt) { char * e = NULL; e = item->u.utype->parser(value, item->ptr); if (e) - opt_failure("User defined type value parsing failed for argument %s: %s\n", OPT_NAME, e); + opt_failure("User defined type value parsing failed for argument %s: %s", OPT_NAME, e); break; } default: @@ -264,13 +327,12 @@ static int opt_shortopt(char ** argv, int index, struct opt_precomputed * pre) { } if (argv[index][chr]) - opt_failure("Unknown option -%c.", item->letter); + opt_failure("Unknown option -%c.", argv[index][chr]); return 0; } #define OPT_TRAVERSE_SECTIONS \ - do { \ while (item->cls == OPT_CL_SECTION) { \ if (stk->next) \ stk = stk->next; \ @@ -284,14 +346,15 @@ static int opt_shortopt(char ** argv, int index, struct opt_precomputed * pre) { item = item->u.section->opt; \ } \ if (item->cls == OPT_CL_END) { \ - if (!stk) break; \ + if (!stk->prev) break; \ item = stk->this; \ stk = stk->prev; \ continue; \ - } \ - } while (0) + } void opt_parse(const struct opt_section * options, char ** argv, opt_positional * callback) { + opt_section_root = options; + struct opt_stack { struct opt_item * this; struct opt_stack * prev; @@ -302,8 +365,9 @@ void opt_parse(const struct opt_section * options, char ** argv, opt_positional stk->next = NULL; struct opt_precomputed * pre = alloca(sizeof(*pre)); + memset(pre, 0, sizeof (*pre)); - int count; + int count = 0; for (struct opt_item * item = options->opt; ; item++) { OPT_TRAVERSE_SECTIONS; @@ -324,13 +388,7 @@ void opt_parse(const struct opt_section * options, char ** argv, opt_positional pre->opts[pre->opt_count++] = opt; if (item->letter) pre->shortopt[(int) item->letter] = opt; - if (!(opt->flags & OPT_VALUE_FLAGS) && - (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER)) { - fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name); - ASSERT(0); - } - else - opt->flags |= opt_default_value_flags[item->cls]; + OPT_ADD_DEFAULT_ITEM_FLAGS(item, opt->flags); } } @@ -429,8 +487,8 @@ static struct cf_user_type teapot_temperature_t = { static struct opt_section water_options = { OPT_ITEMS { - OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"), - OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "Use water with gas"), + OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "\tAmount of water (in mls)"), + OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "\tUse water with gas"), OPT_END } }; @@ -444,23 +502,23 @@ static struct opt_section help = { OPT_HELP(""), OPT_HELP("Options:"), OPT_HELP_OPTION, - OPT_CALL('V', "version", show_version, NULL, OPT_NO_VALUE, "Show the version"), + OPT_CALL('V', "version", show_version, NULL, OPT_NO_VALUE, "\tShow the version"), OPT_HELP(""), - OPT_BOOL('e', "english-style", english, 0, "English style (with milk)"), - OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"), - OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "Standard teapot"), - OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "Exclusive teapot"), - OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "Transparent glass teapot"), - OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "Use user's hands as a teapot (a bit dangerous)"), + OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"), + OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "\tAmount of sugar (in teaspoons)"), + OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "\tStandard teapot"), + OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "\tExclusive teapot"), + OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "\tTransparent glass teapot"), + OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "\tUse user's hands as a teapot (a bit dangerous)"), OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE, - "Wanted final temperature of the tea to be served\n" - "\t\tSupported scales:\tCelsius [60C], Fahrenheit [140F]," - "\t\t\tKelvin [350K], Rankine [600R] and Reaumur [50Re]" + "\tWanted final temperature of the tea to be served\n" + "\t\tSupported scales: Celsius [60C], Fahrenheit [140F],\n" + "\t\t Kelvin [350K], Rankine [600R] and Reaumur [50Re]\n" "\t\tOnly integer values allowed."), - OPT_INC('v', "verbose", verbose, 0, "Verbose (the more -v, the more verbose)"), - OPT_INC('q', "quiet", verbose, OPT_DECREMENT, "Quiet (the more -q, the more quiet)"), - OPT_INT('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"), - OPT_BOOL('p', "pray", pray, 0, "Pray before boiling"), + OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"), + OPT_INC('q', "quiet", verbose, OPT_DECREMENT, "\tQuiet (the more -q, the more quiet)"), + OPT_INT('b', "black-magic", black_magic, 0, "\tUse black magic to make the tea extraordinary delicious"), + OPT_BOOL('p', "pray", pray, 0, "\tPray before boiling"), OPT_HELP(""), OPT_HELP("Water options:"), OPT_SECTION(water_options), diff --git a/ucw/opt.h b/ucw/opt.h index 8e6994df..611b40ce 100644 --- a/ucw/opt.h +++ b/ucw/opt.h @@ -42,7 +42,6 @@ struct opt_item { union opt_union { struct opt_section * section; // subsection for OPT_SECTION int value; // value for OPT_SWITCH - const char * help2; // second value for OPT_HELP2 void (* call)(struct opt_item * opt, const char * value, void * data); // function to call for OPT_CALL struct cf_user_type * utype; // specification of the user-defined type } u; @@ -78,8 +77,7 @@ struct opt_section { ***/ #define OPT_HELP_OPTION OPT_CALL(0, "help", opt_show_help_internal, NULL, OPT_NO_VALUE, "Show this help") -#define OPT_HELP(line) OPT_HELP2(line, NULL) -#define OPT_HELP2(first, second) { .help = first, .cls = OPT_CL_HELP, .u.help2 = second } // FIXME: remove this +#define OPT_HELP(line) { .help = line, .cls = OPT_CL_HELP } #define OPT_BOOL(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = &target, .help = desc, .flags = fl, .cls = OPT_CL_BOOL, .type = CT_INT } #define OPT_STRING(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, char **), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_STRING } #define OPT_U64(shortopt, longopt, target, fl, desc) { .letter = shortopt, .name = longopt, .ptr = CHECK_PTR_TYPE(&target, u64 *), .help = desc, .flags = fl, .cls = OPT_CL_STATIC, .type = CT_U64 } @@ -129,8 +127,8 @@ static uns opt_default_value_flags[] = { [OPT_CL_HELP] = 0 }; -extern struct opt_section * opt_section_root; -void opt_help_noexit_internal(struct opt_section * help); +extern const struct opt_section * opt_section_root; +void opt_help_noexit_internal(const struct opt_section * help); static void opt_help_noexit(void) { opt_help_noexit_internal(opt_section_root);