* 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).