Commit ee71e771 ee71e7712297f5e2ca6ed38fce4a0fc1e72ca5d0 by Sergey Poznyakoff

Allow for multiple argument sets in help output.

Alternative invocations follow the main one, each on a separate line,
preceded by " or: ".

* include/mailutils/cli.h (mu_cli_setup) <prog_alt_args>: New member.
* include/mailutils/opt.h (mu_parseopt) <po_prog_args>: Change type.
(mu_program_usage): Change signature.
* include/mailutils/stream.h (MU_IOCTL_WORDWRAP_GET_OFFSET): Rename to
MU_IOCTL_WORDWRAP_GET_COLUMN. All uses changed.
* libmailutils/cli/cli.c (mu_cli): Construct po.po_prog_args from
prog_args and prog_alt_args.
* libmailutils/opt/help.c (move_margin): Remove.
(print_program_usage): New static function.
(mu_program_usage): Second argument instructs how to display options.
Main work is done by print_program_usage.
(mu_program_help): Call print_program_usage.
* libmailutils/opt/opt.c: Update.
* libmailutils/stream/wordwrap.c (set_margin): Avoid unnecessary flushes.

* libmailutils/tests/parseopt.c (MU_PARSEOPT_PROG_ARGS): Alternative
argument sets are separated by the pipe sign.
* libmailutils/tests/Makefile.am: Add new testcase.
* libmailutils/tests/testsuite.at: Likewise.
* libmailutils/tests/parseopt_help04.at: Add MU_PARSEOPT_PROG_ARGS
to keywords.
* libmailutils/tests/parseopt_help12.at: New file.

* libmailutils/tests/tcli.c (cli): Add alternative arguments.

* dotlock/dotlock.c: Update.
* mimeview/mimeview.c: Likewise.
* sieve/sieve.c: Likewise.
1 parent 2a28ab58
......@@ -93,6 +93,7 @@ static struct mu_cli_setup cli = {
dotlock_cfg_param,
N_("GNU dotlock -- lock mail spool files."),
N_("FILE"),
NULL,
N_("Returns 0 on success, 3 if locking the file fails because\
it's already locked, and 1 if some other kind of error occurred."),
MU_DL_EX_ERROR,
......
......@@ -43,6 +43,7 @@ struct mu_cli_setup
struct mu_cfg_param *cfg; /* Configuration parameters */
char *prog_doc; /* Program documentation string */
char *prog_args; /* Program arguments string */
char const **prog_alt_args; /* Alternative arguments string */
char *prog_extra_doc; /* Extra documentation. This will be
displayed after options. */
int ex_usage; /* If not 0, exit code on usage errors */
......
......@@ -125,7 +125,7 @@ struct mu_parseopt
/* Informational: */
char const *po_prog_name;
char const *po_prog_doc;
char const *po_prog_args;
char const **po_prog_args;
char const *po_bug_address;
char const *po_package_name;
char const *po_package_url;
......@@ -172,7 +172,7 @@ unsigned mu_parseopt_getcolumn (const char *name);
void mu_option_describe_options (mu_stream_t str,
struct mu_option **optbuf, size_t optcnt);
void mu_program_help (struct mu_parseopt *p, mu_stream_t str);
void mu_program_usage (struct mu_parseopt *p, mu_stream_t str);
void mu_program_usage (struct mu_parseopt *p, int optsummary, mu_stream_t str);
void mu_program_version (struct mu_parseopt *po, mu_stream_t str);
void mu_option_set_value (struct mu_parseopt *po, struct mu_option *opt,
......
......@@ -208,11 +208,16 @@ enum mu_buffer_type
((n) == MU_TRANSPORT_INPUT || (n) == MU_TRANSPORT_OUTPUT)
/* Word wrapper streams */
/* Get left margin. */
#define MU_IOCTL_WORDWRAP_GET_MARGIN 0
/* Set left margin */
#define MU_IOCTL_WORDWRAP_SET_MARGIN 1
/* Shift left margin relative to current position */
#define MU_IOCTL_WORDWRAP_MOVE_MARGIN 2
/* Set left margin for the next line */
#define MU_IOCTL_WORDWRAP_SET_NEXT_MARGIN 3
#define MU_IOCTL_WORDWRAP_GET_OFFSET 4
/* Get current column */
#define MU_IOCTL_WORDWRAP_GET_COLUMN 4
struct mu_nullstream_pattern
{
......
......@@ -336,6 +336,10 @@ mu_cli (int argc, char **argv, struct mu_cli_setup *setup, char **capa,
struct mu_cfg_parse_hints hints;
struct mu_option **optv;
mu_list_t com_list;
#define DFLARGC 2
char const *dfl_args[DFLARGC];
char **args = NULL;
size_t argcnt;
/* Set up defaults */
if (setup->ex_usage == 0)
......@@ -374,7 +378,28 @@ mu_cli (int argc, char **argv, struct mu_cli_setup *setup, char **capa,
if (setup->prog_args)
{
po.po_prog_args = setup->prog_args;
size_t i;
argcnt = 1;
if (setup->prog_alt_args)
{
for (i = 0; setup->prog_alt_args[i]; i++)
argcnt++;
}
if (argcnt < DFLARGC)
po.po_prog_args = dfl_args;
else
{
args = mu_calloc (argcnt + 1, sizeof (args[0]));
po.po_prog_args = (char const **) args;
}
po.po_prog_args[0] = setup->prog_args;
for (i = 1; i < argcnt; i++)
po.po_prog_args[i] = setup->prog_alt_args[i-1];
po.po_prog_args[i] = NULL;
flags |= MU_PARSEOPT_PROG_ARGS;
}
......@@ -439,5 +464,8 @@ mu_cli (int argc, char **argv, struct mu_cli_setup *setup, char **capa,
mu_cfg_destroy_tree (&parse_tree);
free (optv);
free (args);
mu_parseopt_free (&po);
}
......
......@@ -204,16 +204,9 @@ static void
get_offset (mu_stream_t str, unsigned *offset)
{
mu_stream_ioctl (str, MU_IOCTL_WORDWRAPSTREAM,
MU_IOCTL_WORDWRAP_GET_OFFSET,
MU_IOCTL_WORDWRAP_GET_COLUMN,
offset);
}
static void
move_margin (mu_stream_t str, int margin)
{
mu_stream_ioctl (str, MU_IOCTL_WORDWRAPSTREAM,
MU_IOCTL_WORDWRAP_MOVE_MARGIN,
&margin);
}
static void
print_opt_arg (mu_stream_t str, struct mu_option *opt, int delim)
......@@ -333,6 +326,9 @@ mu_option_describe_options (mu_stream_t str,
}
}
static void print_program_usage (struct mu_parseopt *po, int optsum,
mu_stream_t str);
void
mu_program_help (struct mu_parseopt *po, mu_stream_t outstr)
{
......@@ -347,14 +343,7 @@ mu_program_help (struct mu_parseopt *po, mu_stream_t outstr)
abort ();//FIXME
}
mu_stream_printf (str, "%s", _("Usage:"));
if (po->po_prog_name)
mu_stream_printf (str, " %s ", po->po_prog_name);
move_margin (str, 0);
mu_stream_printf (str, "[%s]...", _("OPTION"));
if (po->po_prog_args)
mu_stream_printf (str, " %s", gettext (po->po_prog_args));
mu_stream_printf (str, "\n");
print_program_usage (po, 0, str);
if (po->po_prog_doc)
{
......@@ -423,10 +412,9 @@ cmpidx_long (const void *a, const void *b)
return strcmp (ap->opt_long, bp->opt_long);
}
void
mu_program_usage (struct mu_parseopt *po, mu_stream_t outstr)
static void
option_summary (struct mu_parseopt *po, mu_stream_t str)
{
int rc;
unsigned i;
unsigned *idxbuf;
unsigned nidx;
......@@ -434,23 +422,10 @@ mu_program_usage (struct mu_parseopt *po, mu_stream_t outstr)
struct mu_option **optbuf = po->po_optv;
size_t optcnt = po->po_optc;
mu_stream_t str;
init_usage_vars (po);
rc = mu_wordwrap_stream_create (&str, outstr, 0, rmargin);
if (rc)
{
abort ();//FIXME
}
option_tab = optbuf;
idxbuf = mu_calloc (optcnt, sizeof (idxbuf[0]));
mu_stream_printf (str, "%s %s ", _("Usage:"), po->po_prog_name);
set_next_margin (str, usage_indent);
/* Print a list of short options without arguments. */
for (i = nidx = 0; i < optcnt; i++)
if (MU_OPTION_IS_VALID_SHORT_OPTION (optbuf[i]) && !optbuf[i]->opt_arg)
......@@ -517,12 +492,66 @@ mu_program_usage (struct mu_parseopt *po, mu_stream_t outstr)
}
}
free (idxbuf);
}
static void
print_program_usage (struct mu_parseopt *po, int optsum, mu_stream_t str)
{
char const *usage_text;
char const **arg_text;
size_t i;
usage_text = _("Usage:");
if (po->po_flags & MU_PARSEOPT_PROG_ARGS)
mu_stream_printf (str, " %s", _(po->po_prog_args));
arg_text = po->po_prog_args;
else
arg_text = NULL;
i = 0;
mu_stream_destroy (&str);
do
{
mu_stream_printf (str, "%s %s ", usage_text, po->po_prog_name);
set_next_margin (str, usage_indent);
free (idxbuf);
if (optsum)
{
option_summary (po, str);
optsum = 0;
}
else
mu_stream_printf (str, "[%s]...", _("OPTION"));
if (arg_text)
{
mu_stream_printf (str, " %s\n", gettext (arg_text[i]));
if (i == 0)
usage_text = _("or: ");
set_margin (str, 2);
i++;
}
else
mu_stream_flush (str);
}
while (arg_text && arg_text[i]);
}
void
mu_program_usage (struct mu_parseopt *po, int optsum, mu_stream_t outstr)
{
int rc;
mu_stream_t str;
init_usage_vars (po);
rc = mu_wordwrap_stream_create (&str, outstr, 0, rmargin);
if (rc)
{
abort ();//FIXME
}
print_program_usage (po, optsum, str);
mu_stream_destroy (&str);
}
void
......
......@@ -87,7 +87,7 @@ fn_help (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
static void
fn_usage (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
{
mu_program_usage (po, mu_strout);
mu_program_usage (po, 1, mu_strout);
exit (EXIT_SUCCESS);
}
......
......@@ -178,15 +178,17 @@ set_margin (mu_stream_t stream, unsigned lmargin, int off)
if (lmargin >= str->right_margin)
return EINVAL;
str->left_margin = lmargin;
if (lmargin < str->offset ||
(str->offset > 0 && str->buffer[str->offset - 1] == '\n'))
if (str->offset > str->left_margin
&& (lmargin < str->offset || str->buffer[str->offset - 1] == '\n'))
{
str->left_margin = lmargin;
_wordwrap_flush (stream);
}
else if (lmargin > str->offset)
else
{
if (lmargin > str->offset)
memset (str->buffer + str->offset, ' ', lmargin - str->offset);
str->left_margin = lmargin;
str->offset = lmargin;
}
......@@ -236,7 +238,7 @@ _wordwrap_ctl (mu_stream_t stream, int code, int opcode, void *arg)
else
return set_margin (stream, str->offset, *(int*)arg);
case MU_IOCTL_WORDWRAP_GET_OFFSET:
case MU_IOCTL_WORDWRAP_GET_COLUMN:
if (!arg)
return EINVAL;
*(unsigned*)arg = str->offset;
......
......@@ -18,6 +18,7 @@ imapio
listop
mailcap
mimehdr
modmesg
modtofsaf
msgset
parseopt
......@@ -32,4 +33,5 @@ tempfile
url-comp
url-parse
wicket
wordwrap
wsp
......
......@@ -144,6 +144,7 @@ TESTSUITE_AT = \
parseopt_help09.at\
parseopt_help10.at\
parseopt_help11.at\
parseopt_help12.at\
prop.at\
scantime.at\
strftime.at\
......
......@@ -77,8 +77,43 @@ struct parseopt_param
int flag;
mu_c_type_t type;
size_t off;
void (*setfn) (struct parseopt_param *param, char const *val, void *target);
};
static void
set_prog_args (struct parseopt_param *param, char const *str, void *target)
{
char ***args_ptr = target;
char **args;
char *p;
size_t size, i;
size = 1;
for (i = 0; str[i]; i++)
if (str[i] == '\n')
size++;
args = mu_calloc (size + 1, sizeof (args[0]));
i = 0;
while (1)
{
size_t len = strcspn (str, "|");
p = mu_alloc (len + 1);
memcpy (p, str, len);
p[len] = 0;
args[i++] = p;
str += len;
if (str[0])
++str;
else
break;
}
args[i] = NULL;
*args_ptr = args;
}
static struct parseopt_param parseopt_param[] = {
{ "MU_PARSEOPT_ARGV0", MU_PARSEOPT_ARGV0, mu_c_void },
{ "MU_PARSEOPT_IGNORE_ERRORS", MU_PARSEOPT_IGNORE_ERRORS, mu_c_void },
......@@ -93,7 +128,8 @@ static struct parseopt_param parseopt_param[] = {
{ "MU_PARSEOPT_PROG_DOC", MU_PARSEOPT_PROG_DOC,
mu_c_string, mu_offsetof(struct mu_parseopt, po_prog_doc) },
{ "MU_PARSEOPT_PROG_ARGS", MU_PARSEOPT_PROG_ARGS,
mu_c_string, mu_offsetof(struct mu_parseopt, po_prog_args) },
mu_c_string, mu_offsetof(struct mu_parseopt, po_prog_args),
set_prog_args },
{ "MU_PARSEOPT_BUG_ADDRESS", MU_PARSEOPT_BUG_ADDRESS,
mu_c_string, mu_offsetof(struct mu_parseopt, po_bug_address) },
{ "MU_PARSEOPT_PACKAGE_NAME", MU_PARSEOPT_PACKAGE_NAME,
......@@ -129,7 +165,11 @@ main (int argc, char *argv[])
if (val)
{
flags |= param->flag;
if (param->type != mu_c_void)
if (param->setfn)
{
param->setfn (param, val, ((char*)&po + param->off));
}
else if (param->type != mu_c_void)
{
char *errmsg;
int rc = mu_str_to_c (val, param->type,
......
......@@ -15,7 +15,7 @@
# along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
AT_SETUP([MU_PARSEOPT_PROG_ARGS])
AT_KEYWORDS([parseopt parseopt_help parseopt_help04])
AT_KEYWORDS([parseopt parseopt_help MU_PARSEOPT_PROG_ARGS parseopt_help04])
AT_CHECK([
PARSEOPT_DEFAULT
MU_PARSEOPT_PROG_ARGS="SOME MORE ARGS" parseopt --help
......
# This file is part of GNU Mailutils. -*- Autotest -*-
# Copyright (C) 2016 Free Software Foundation, Inc.
#
# GNU Mailutils is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3, or (at
# your option) any later version.
#
# GNU Mailutils is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
AT_SETUP([MU_PARSEOPT_PROG_ARGS (alternative)])
AT_KEYWORDS([parseopt parseopt_help MU_PARSEOPT_PROG_ARGS parseopt_help12])
AT_CHECK([
PARSEOPT_DEFAULT
MU_PARSEOPT_PROG_ARGS="SOME MORE ARGS|ALTERNATIVE ARGS|ANOTHER ARGS" parseopt --help
],
[0],
[Usage: parseopt [[OPTION]]... SOME MORE ARGS
or: parseopt [[OPTION]]... ALTERNATIVE ARGS
or: parseopt [[OPTION]]... ANOTHER ARGS
Group A
-a, --all no arguments to this one
-f, --file=FILE set file name
-o, --optional[[=FILE]] optional argument
-x short-only option
Group B
-d, -v, --debug, --verbose another option
-F, --find=VALUE find VALUE
-j, --jobs=N sets numeric value
-?, --help give this help list
--usage give a short usage message
Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.
])
AT_CLEANUP
......@@ -37,11 +37,14 @@ static struct mu_cfg_param config[] = {
{ NULL }
};
char const *alt_args[] = { "ALT ARGUMENTS 1", "ALT ARGUMENTS 2", NULL };
struct mu_cli_setup cli = {
options,
config,
"Tests standard command line interface",
"ARGUMENTS"
"ARGUMENTS",
alt_args
};
static char **
......
......@@ -121,6 +121,7 @@ m4_include([parseopt_help08.at])
m4_include([parseopt_help09.at])
m4_include([parseopt_help10.at])
m4_include([parseopt_help11.at])
m4_include([parseopt_help12.at])
AT_BANNER([Standard streams])
m4_include([strin.at])
......
......@@ -145,6 +145,7 @@ struct mu_cli_setup cli = {
mimeview_cfg_param,
N_("GNU mimeview -- display files, using mailcap mechanism."),
N_("FILE [FILE ...]"),
NULL,
N_("Debug flags are:\n\
g - Mime.types parser traces\n\
l - Mime.types lexical analyzer traces\n\
......
......@@ -248,6 +248,7 @@ static struct mu_cli_setup cli = {
sieve_cfg_param,
N_("GNU sieve -- a mail filtering tool."),
"SCRIPT",
NULL,
N_("Debug flags:\n\
g - main parser traces\n\
T - mailutils traces (same as --debug-level=sieve.trace0-trace1)\n\
......