discuss@mandoc.bsd.lv
 help / color / mirror / Atom feed
* mdoc synopsis style for more than one mandatory, mutually exclusive options?
@ 2021-07-19 22:48 Stephen Gregoratto
  2021-07-20  0:34 ` Ingo Schwarze
  0 siblings, 1 reply; 4+ messages in thread
From: Stephen Gregoratto @ 2021-07-19 22:48 UTC (permalink / raw)
  To: discuss

I am working on a utility that takes input from different sources:

1. Arguments after options parsing (i.e., after --).
2. From the named file after the `-R` flag.
3. From the named file after the `-I` flag.

The program will quit if input == 0, so at least one of these options
must be used. And there can be multiple instances of each (i.e., -R and
-I can be specified more than once).

My question is: how can I format this in the synopsis without it being
overwhelming? Semantically, .Op is out because it isn't optional. I've
seen other utilities use something like

  (a | -F b)...

instead, but I've never seen this form in a manpage before.
-- 
Stephen Gregoratto
--
 To unsubscribe send an email to discuss+unsubscribe@mandoc.bsd.lv


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: mdoc synopsis style for more than one mandatory, mutually exclusive options?
  2021-07-19 22:48 mdoc synopsis style for more than one mandatory, mutually exclusive options? Stephen Gregoratto
@ 2021-07-20  0:34 ` Ingo Schwarze
  2021-07-20  4:10   ` Stephen Gregoratto
  0 siblings, 1 reply; 4+ messages in thread
From: Ingo Schwarze @ 2021-07-20  0:34 UTC (permalink / raw)
  To: Stephen Gregoratto; +Cc: discuss

Hi Stephen,

Stephen Gregoratto wrote on Tue, Jul 20, 2021 at 08:48:54AM +1000:

> I am working on a utility that takes input from different sources:
> 
> 1. Arguments after options parsing (i.e., after --).
> 2. From the named file after the `-R` flag.
> 3. From the named file after the `-I` flag.
> 
> The program will quit if input == 0, so at least one of these options
> must be used. And there can be multiple instances of each (i.e., -R and
> -I can be specified more than once).
> 
> My question is: how can I format this in the synopsis without it being
> overwhelming? Semantically, .Op is out because it isn't optional.

Don't over-think the SYNOPSIS.  It cannot express everything.
If something cannot be fully expressed in the SYNOPSIS, write the
SYNOPSIS such that it isn't wrong and then explain the additional
constraints in the DESCRIPTION.

From what you say above, it is clear you need at least this:

.Nm
.Op Fl I ifile
.Op Fl R rfile
.Op Ar argument ...

Do not use ifile, rfile, and argument for the placeholders;
use expressive names instead.  I cannot suggest any because i do
not know ehat the two kinds of files contain and what the arguments
mean.

Then, in the DESCRIPTION, after explaining what the basic purpose
of the utility is, say something like

  At least one source of input, i.e. at least one
  .Fl I
  option,
  .Fl R
  option, or
  .Ar argument
  is required.

If it matters, add a sentence like this one here:

  Input sources are processed in the order given on the command line.

At the end of .It Fl I , say something like

  Multiple
  .Fl I
  options can be provided

And if that matters and you did not already say it earlier, something like:

  and are processed in the given order.


All that said, also consider command line user interface design;
difficulty in writing a manual can be a symptom of missed opportunities
of making the UI simpler or more uniform.

For example:

 * Many utilities optionally taking input and output file names
   as option arguments read from stdin and write to stdout by
   default, to allow using them in pipelines.  Would that be
   beneficial for your program?  Is it really necessary that
   the program does nothing useful unless it is given at least
   one option or argument?

 * If "ifile" and "argument" serve a similar purpose, why is one
   and option argument and the other an argument proper?
   Would the UI become easier to understand by choosing another
   option letter and making "argument" an option argument, too?
   That would also allow to first process a file, then an argument,
   then another file.
   For example, sed(1) supports both "-f command_file" and
   "-e command".  Whether supporting both "sed command file"
   and "sed -e command file" was a good idea (syntactic sugar)
   or a bad one (needless variation causing complexity) is another
   question; for sed(1), it may be warranted because "sed command file"
   is really the dominant use case and -e and -f are needed more rarely.
   But the dominant use case may be different for your utility.
   For example, i think supporting "man -s 2 pledge" would have
   been enough; that "man 2 pledge" also works is useless UI bloat
   because the dominant use case is just "man pledge" and in those
   few cases where the section needs to be specified, typing "-s"
   wouldn't have been annoying.

> I've seen other utilities use something like
> 
>   (a | -F b)...
> 
> instead, but I've never seen this form in a manpage before.

Using parentheses for alternatives isn't unusual in GNU manuals,
but you won't find it in OpenBSD manuals because it's an attempt
to express complexity that can't really be expressed very well in
the SYNOPSIS, and also because it almost always conflicts with the
rule to list options without arguments first in alphabetical order,
followed by options with arguments in alphabetical order, followed
by positional arguments.

Yours,
  Ingo
--
 To unsubscribe send an email to discuss+unsubscribe@mandoc.bsd.lv


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: mdoc synopsis style for more than one mandatory, mutually exclusive options?
  2021-07-20  0:34 ` Ingo Schwarze
@ 2021-07-20  4:10   ` Stephen Gregoratto
  2021-07-20 13:02     ` Ingo Schwarze
  0 siblings, 1 reply; 4+ messages in thread
From: Stephen Gregoratto @ 2021-07-20  4:10 UTC (permalink / raw)
  To: Ingo Schwarze; +Cc: Stephen Gregoratto, discuss

Hi Ingo,

Thanks for your recommendations. The program I'm working on is an
implementation of age[1], a encryption program. It's supposed to act as
just another UNIX-style filter that just works™.

My implementation is different in that I'm not strictly following the
reference's style of arguments, as I find them too complicated for my
tastes. It commits the "sin" of arguments taking on different meanings
in different contexts. You make a good point about needless complexity,
and I've tried to structure my implementation to reduce this as much as
possible, mainly by using subcommands.


Here's the full synopsis of the previous scenario, in context:

  zage encrypt [-a] [-i file] [-o file] \
    -R recipient-file -I identity-file recipients ...
  zage encrypt [-a] [-i file] [-o file] -p

Here "recipients" are public keys, in two forms: age X25519 keys and
OpenSSH Ed25519 keys. They're short enough that it's reasonable to
expect users to paste them in as arguments. -R denotes files
containing these recipient types, one per line. This means that users
can pass their authorized_keys file, and encrypt to any ssh-ed25519 key
in said file. I kept going back and forth between having -R only
specified once or more than, but I decided to stick with the latter
since it leads to cute invocations like:

-R ~/.ssh/id_ed25519.pub -R <(curl -s https://github.com/ischwarze.keys)

-I is similar to -R, except it reads secret keys, and thus has to
compute the public key of each. This one is a holdover from the
reference implementation, and I'm not so sure about keeping it around.

-i and -o are the usual input and output files, defaulting to stdin/out
if not given. -a is for PEM/ASCII armour, and -p is a special case for
password encryption.

Hope that clears things up. I think I'll stick to your advice about
keeping the SYNOPSIS simple, and explain in detail later on (probably in
a subheading to keep things neat).

[1] https://age-encryption.org/v1
-- 
Stephen Gregoratto
--
 To unsubscribe send an email to discuss+unsubscribe@mandoc.bsd.lv


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: mdoc synopsis style for more than one mandatory, mutually exclusive options?
  2021-07-20  4:10   ` Stephen Gregoratto
@ 2021-07-20 13:02     ` Ingo Schwarze
  0 siblings, 0 replies; 4+ messages in thread
From: Ingo Schwarze @ 2021-07-20 13:02 UTC (permalink / raw)
  To: Stephen Gregoratto; +Cc: discuss

Hi Stephen,

Stephen Gregoratto wrote on Tue, Jul 20, 2021 at 02:10:38PM +1000:

> Thanks for your recommendations. The program I'm working on is an
> implementation of age[1], an encryption program. It's supposed to
> act as just another UNIX-style filter that just works.

I see, thanks for clarifying.

> My implementation is different in that I'm not strictly following the
> reference's style of arguments, as I find them too complicated for my
> tastes. It commits the "sin" of arguments taking on different meanings
> in different contexts.

I agree that the API shown at

  https://age-encryption.org/v1

is atrocious, and so is the quality of the documentation.
Both are highly confusing.

> You make a good point about needless complexity, and I've tried
> to structure my implementation to reduce this as much as
> possible, mainly by using subcommands.

IMHO, subcommands only make sense when there is a significant number
of them, like in bgpctl(8) or smtpctl(8), but here, i can only think
of three, and in that case, simply using options without arguments
like in tar(1) may be sufficient:

  -e  encrypt file
  -d  decrypt file
  -g  generate key

Consider simplicity in typical everyday use.  I guess the dominant
use case will be encryping with a single key.  While pasting the
public key to the command line seems possible, i don't expect it to
be the most frequent use.  Which of the following is less
cumbersome:

  ... | zage encrypt -R ~/.ssh/id_ed25519.pub | ...
  ... | zage -eR ~/.ssh/id_ed25519.pub | ...

What are people expected to use this for?  Encrypting files for
themselves?  In that case, -r/-R/-I to be re-typed again and again
would be horrible, that would be a matter for a configuration file.
For sending files to friends?  In that case, i would want to type
something like

  zage -er sgregoratto secret.file >> message.txt

I must admit i'm not quite sure what the intended use is.  If there
are multiple scenarios, it ought to work well for all of them,
without being cumbersome for any of them, yet the syntax still
ought to be clear and simple.

> Here's the full synopsis of the previous scenario, in context:
> 
>   zage encrypt [-a] [-i file] [-o file] \
>     -R recipient-file -I identity-file recipients ...
>   zage encrypt [-a] [-i file] [-o file] -p
> 
> Here "recipients" are public keys, in two forms: age X25519 keys and
> OpenSSH Ed25519 keys.

One lesson from the past is that algorithms change.  Do i understand
correctly that the reason why the CLI provides no option to choose
the algorithm is because each key file already states the algorithm?
If so, that's nicely simple and flexible design.

Your "recipient" argument (which is a misnomer by the way, it's
really a pubkey, not a recipient) is always long (about 60-70
characters), so pasting it will be an uncommon use case, and whether
the syntax used for it is short or long does not matter at all.
So making it an argument is a clear misdesign.  It should definitely
be an option argument - both of the following are highly cumbersome,
so it really doesn't matter, and *this* should most certainly *not*
block the argument space, which is much more precious than option
argument space:

  ... | zage -er some_very_long_gibberish | ...
  ... | zage -e some_very_long_gibberish | ...

> They're short enough that it's reasonable to
> expect users to paste them in as arguments. -R denotes files
> containing these recipient types, one per line. This means that users
> can pass their authorized_keys file, and encrypt to any ssh-ed25519 key
> in said file. I kept going back and forth between having -R only
> specified once or more than, but I decided to stick with the latter
> since it leads to cute invocations like:
> 
> -R ~/.ssh/id_ed25519.pub -R <(curl -s https://github.com/ischwarze.keys)

From a UI design perspective, having an option that can be specified
multiple times is perfectly fine, so i certainly don't recommend
against that, even though i don't understand what the point is in
encrypting with more than one key of the same person.
Doesn't that just multiply the total size of the output by the number
of (equivalent) keys used?  And if you want to send the same file to
five different persons and consequently encode with five keys, why
would you want the encrypted output to go to the same output file?
Wouldn't you need five separate output files, to be sent into five
different directions?

> -I is similar to -R, except it reads secret keys, and thus has to
> compute the public key of each. This one is a holdover from the
> reference implementation, and I'm not so sure about keeping it around.

Actually, i tend to recommend deleting the option and the functionality
it provides, for two reasons.  First and more obvious reason: every
reasonable key generation tool generates the private and public key
files side by side.  The private one is never supposed to be moved
anywhere else.  The public one can be *copied* away as often as you
like, but *moving* it away or deleting it would be utterly stupid.
Even if it does accidentylly get deleted - then again, how can that
happen in a directory as delicate as one containing a private key -
that should almost certainly be fixed by re-crating it, using an
appropriate tool, and that's totally unrelated to zage.  So there
is no sane use case for -I.

The second, more important argument is that you should not encourage
people to gratuitiously use the private key file when it's not needed,
in particularly not manually, because every manual use of a private
key file poses a risk of misusing the private key and accidentally
compromising it, most likely without even noticing the compromise.

Besides, everything you can delete from an API is a significant gain
in API quality.  So, if you can delete an option without significant
loss of functionality, do it.  There is no loss whatsoever here...

> -i and -o are the usual input and output files, defaulting to stdin/out
> if not given.

That is definitely good design.

> -a is for PEM/ASCII armour, and -p is a special case for
> password encryption.

That sounds reasonable, except that i wonder: which algorithm does
password encryption use?  Hopefully not a hard-coded one, which would
soon become obsolete?  And if a varible one, how does the program
select it?

> Hope that clears things up. I think I'll stick to your advice about
> keeping the SYNOPSIS simple, and explain in detail later on (probably in
> a subheading to keep things neat).

Sure.  Note that the discussion started as a discussion about markup,
but the lion's share of the discussion is actually about UI design.
That's typical!  Always be prepared to consider UI design extremely
carefully, even when it may seem simple at first.  Work spent on
good UI design is almost never wasted (and almost never quick).
Once you have good UI design, documenting the UI tends to be very
straightforward, and most markup questions tend to become trivial,
too.

It does happen that writing documentation is arduous, and that even
choosing adequate markup is hard - but usually only when the UI
design was seriously botched in the first place.

Yours,
  Ingo
--
 To unsubscribe send an email to discuss+unsubscribe@mandoc.bsd.lv


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-07-20 13:03 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-19 22:48 mdoc synopsis style for more than one mandatory, mutually exclusive options? Stephen Gregoratto
2021-07-20  0:34 ` Ingo Schwarze
2021-07-20  4:10   ` Stephen Gregoratto
2021-07-20 13:02     ` Ingo Schwarze

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).