Re: ksh tab completion bug

2020-11-10 Thread Michael Forney
Hi Anton,

Thanks for providing those links to the past discussion.

Anton Lindqvist  wrote:
> As I would address this, the numbers of arguments passed to the
> completion related routines is already painfully^W long. I would rather
> take a step back and introduce a `struct globstate' (just an example,
> could be renamed) which includes all the necessary parameters. Getting
> such refactoring in place first would make the diff even smaller.

After thinking about this some more, I think the word length is not
really the right thing to check, even if you account for escape
characters. What we really want to know is whether or not the current
word was modified by completing up to a common prefix.

Here is another (slightly contrived) case where completion is not
performed, even when backslashes are not involved:

Say you have a user somelonguser, whose home directory is /a, and
there are two files in that directory, /a/test1 and /a/test2. ksh
will not complete ~somelonguser/t to /a/test since the expanded
word is shorter than the original.

This example works on vi completion for the same reason as before.

I took a stab at changing do_complete to use a similar approach as
vi completion (diff below), and it is a bit simpler (only modifies
emacs.c and net removal). However, I still have some concerns:

* There is a slight behavior change when you have test1 and test2,
  and manually type the entire common prefix. Previously, the options
  would be displayed on the first , but now they are only
  displayed on the second .
* The use of a global variable to track whether the word was modified
  since the last completion is a bit messy. vi.c contains a bunch
  of `expanded = NONE` scattered throughout the file. It is quite
  possible that the two I added to emacs.c are not sufficient for
  some editing operations.

Here's another idea for a fix that might address those issues:

* After the call to x_cf_glob, save the part of the buffer between
  start and end in some temporary location.
* Always replace start through end with the longest common prefix.
* After replacement, compare the escaped word with the saved original
  word. If it matches exactly, print the replacement options.

This would avoid the global variable, but I'm not sure how to
determine the new end position after x_escape() since it just calls
x_do_ins() directly.

Though not quite related, I noticed that x_expand does not call
x_free_words before it returns, so it leaks memory in ATEMP. But
since that gets reclaimed after each command, it's probably not
worth worrying about.

> Also, emacs mode has its own regress suite located in
> regress/bin/ksh/edit/emacs.sh. Tricky logic like this should be covered
> in my opinion. Below is a rough diff how to test file name completion.

I'm using OpenBSD's ksh through oksh, which does not contain the
regress tests. I think I'd have to get an OpenBSD VM up and running
in order to contribute a test case.

> Michael, is this something you would like to continue work on?

I would like to see this issue fixed, but now that I have a local
patch that works for me, my motivation is dwindling. I'm happy to
fix any details with my diff, but if there is substantial refactoring
involved in order to merge, then probably not.

I also have no particular attachment to the diffs I wrote. If anyone
has their own idea about a better solution, feel free to go ahead
with that.

diff --git a/emacs.c b/emacs.c
index 2b70992..2ec4fc4 100644
--- a/emacs.c
+++ b/emacs.c
@@ -130,6 +130,7 @@ static  int x_arg_set;
 static char*macro_args;
 static int prompt_skip;
 static int prompt_redraw;
+static int completed;
 
 static int x_ins(char *);
 static voidx_delete(int, int);
@@ -460,6 +461,7 @@ x_ins(char *s)
 
if (x_do_ins(s, strlen(s)) < 0)
return -1;
+   completed = 0;
/*
 * x_zots() may result in a call to x_adjust()
 * we want xcp to reflect the new position.
@@ -566,7 +568,7 @@ x_delete(int nc, int push)
for (cp = x_lastcp(); cp > xcp; )
x_bs(*--cp);
 
-   return;
+   completed = 0;
 }
 
 static int
@@ -1749,7 +1751,6 @@ do_complete(int flags,/* 
XCF_{COMMAND,FILE,COMMAND_FILE} */
int nwords;
int start, end, nlen, olen;
int is_command;
-   int completed = 0;
 
nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf,
, , , _command);
@@ -1759,7 +1760,7 @@ do_complete(int flags,/* 
XCF_{COMMAND,FILE,COMMAND_FILE} */
return;
}
 
-   if (type == CT_LIST) {
+   if (type == CT_LIST || type == CT_COMPLIST && completed) {
x_print_expansions(nwords, words, is_command);
x_redraw(0);
x_free_words(nwords, words);
@@ -1769,27 +1770,20 @@ do_complete(int flags,  /* 
XCF_{COMMAND,FILE,COMMAND_FILE} */
olen = end - start;
nlen = x_longest_prefix(nwords, words);
/* complete */

ksh tab completion bug

2020-11-09 Thread Michael Forney
I noticed some strange behavior of ksh in emacs mode when completing
file names that contain spaces (or other characters that need
escaping).

To illustrate the problem, consider two files 'a b c test1' and
'a b c test2'. ksh will correctly complete `a` and `a\ b\ c\ ` to
the common prefix `a\ b\ c\ test`. However, all of the following
do not get completed:

* `a\ b\ c\ t`
* `a\ b\ c\ te`
* `a\ b\ c\ tes`

I did some debugging, and I think this is due to emacs.c:do_complete()
using `end - start` for olen (which counts the backslashes), but
nlen (the longest prefix length) does not. So the completion only
occurs when the current word is shorter than the common prefix
length minus the number of backslashes in the word.

I don't believe vi.c:complete_word() has this issue since it always
replaces the word with the longest prefix.

I wrote a (only lightly tested) patch to make x_cf_glob return the
unescaped length as well as the start and end positions, and use
that for olen instead of `end - start`. This seems to fix the issue,
but it is longer than I had hoped. Perhaps there is a simpler patch
to make emacs.c:do_complete() use the same approach as vi completion.

diff --git a/bin/ksh/edit.c b/bin/ksh/edit.c
index 3089d195d20..d44afc258ba 100644
--- a/bin/ksh/edit.c
+++ b/bin/ksh/edit.c
@@ -29,7 +29,7 @@ static void check_sigwinch(void);
 
 static int x_file_glob(int, const char *, int, char ***);
 static int x_command_glob(int, const char *, int, char ***);
-static int x_locate_word(const char *, int, int, int *, int *);
+static int x_locate_word(const char *, int, int, int *, int *, int *);
 
 
 /* Called from main */
@@ -524,15 +524,16 @@ x_command_glob(int flags, const char *str, int slen, char 
***wordsp)
(c) == '`' || (c) == '=' || (c) == ':' )
 
 static int
-x_locate_word(const char *buf, int buflen, int pos, int *startp,
+x_locate_word(const char *buf, int buflen, int pos, int *startp, int *endp,
 int *is_commandp)
 {
int p;
-   int start, end;
+   int start, end, len;
 
/* Bad call?  Probably should report error */
if (pos < 0 || pos > buflen) {
*startp = pos;
+   *endp = pos;
*is_commandp = 0;
return 0;
}
@@ -546,10 +547,13 @@ x_locate_word(const char *buf, int buflen, int pos, int 
*startp,
(start > 1 && buf[start-2] == '\\'); start--)
;
/* Go forwards to end of word */
+   len = 0;
for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
if (buf[end] == '\\' && (end+1) < buflen)
end++;
+   len++;
}
+   *endp = end;
 
if (is_commandp) {
int iscmd;
@@ -574,7 +578,7 @@ x_locate_word(const char *buf, int buflen, int pos, int 
*startp,
 
*startp = start;
 
-   return end - start;
+   return len;
 }
 
 static int
@@ -654,14 +658,14 @@ x_try_array(const char *buf, int buflen, const char 
*want, int wantlen,
 
 int
 x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp,
-int *endp, char ***wordsp, int *is_commandp)
+int *endp, int *lenp, char ***wordsp, int *is_commandp)
 {
-   int len;
+   int len, esclen;
int nwords;
char **words = NULL;
int is_command;
 
-   len = x_locate_word(buf, buflen, pos, startp, _command);
+   len = x_locate_word(buf, buflen, pos, startp, endp, _command);
if (!(flags & XCF_COMMAND))
is_command = 0;
/* Don't do command globing on zero length strings - it takes too
@@ -671,10 +675,11 @@ x_cf_glob(int flags, const char *buf, int buflen, int 
pos, int *startp,
if (len == 0 && is_command)
return 0;
 
+   esclen = *endp - *startp;
if (is_command)
-   nwords = x_command_glob(flags, buf + *startp, len, );
-   else if (!x_try_array(buf, buflen, buf + *startp, len, , ))
-   nwords = x_file_glob(flags, buf + *startp, len, );
+   nwords = x_command_glob(flags, buf + *startp, esclen, );
+   else if (!x_try_array(buf, buflen, buf + *startp, esclen, , 
))
+   nwords = x_file_glob(flags, buf + *startp, esclen, );
if (nwords == 0) {
*wordsp = NULL;
return 0;
@@ -683,7 +688,7 @@ x_cf_glob(int flags, const char *buf, int buflen, int pos, 
int *startp,
if (is_commandp)
*is_commandp = is_command;
*wordsp = words;
-   *endp = *startp + len;
+   *lenp = len;
 
return nwords;
 }
diff --git a/bin/ksh/edit.h b/bin/ksh/edit.h
index 0b604cd64fb..f988d92c4b2 100644
--- a/bin/ksh/edit.h
+++ b/bin/ksh/edit.h
@@ -43,7 +43,7 @@ bool  x_mode(bool);
 intpromptlen(const char *, const char **);
 intx_do_comment(char *, int, int *);
 void   x_print_expansions(int, char *const *, int);
-intx_cf_glob(int, const char *, int, int, int 

Re: ksh: Use typedef for function pointer

2020-05-08 Thread Michael Forney
On 2020-05-08, Jeremie Courreges-Anglas  wrote:
> Out of curiosity, do you run (o)ksh on machines where this matters?

Yes, my C compiler (https://sr.ht/~mcf/cproc/) follows the standard
fairly strictly.

> Aside from the increased portability, the code doesn't look worse, and
> fewer uses of void * is nice.  I'll commit this soon if I don't hear
> objections (oks also welcome, obviously).

Thanks!



ksh: Use typedef for function pointer

2020-05-07 Thread Michael Forney
I originally submitted this patch as a portability fix to Brian
Callahan's oksh, but he suggested I submit it here instead.

Conversion of function pointer to void pointer is not allowed in
ISO C, though POSIX requires it for dlsym(). However, here we are
also comparing function pointer to void pointer with the == operator
without using a cast, which is a constraint error[0].

Rather than add a cast, we can just use a typedef here for the
function pointer type, avoiding any C extensions, and adding a bit
of type-safety.

[0] https://port70.net/~nsz/c/c11/n1570.html#6.5.9p2
---
 bin/ksh/emacs.c | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/bin/ksh/emacs.c b/bin/ksh/emacs.c
index aa2cceb657d..b63735956a3 100644
--- a/bin/ksh/emacs.c
+++ b/bin/ksh/emacs.c
@@ -41,8 +41,10 @@ static   Areaaedit;
 #defineKEOL1   /* ^M, ^J */
 #defineKINTR   2   /* ^G, ^C */
 
+typedef int (*kb_func)(int);
+
 struct x_ftab {
-   int (*xf_func)(int c);
+   kb_func xf_func;
const char  *xf_name;
short   xf_flags;
 };
@@ -861,7 +863,7 @@ x_eot_del(int c)
return (x_del_char(c));
 }
 
-static void *
+static kb_func
 kb_find_hist_func(char c)
 {
struct kb_entry *k;
@@ -1315,7 +1317,7 @@ kb_del(struct kb_entry *k)
 }
 
 static struct kb_entry *
-kb_add_string(void *func, void *args, char *str)
+kb_add_string(kb_func func, void *args, char *str)
 {
unsigned intele, count;
struct kb_entry *k;
@@ -1350,7 +1352,7 @@ kb_add_string(void *func, void *args, char *str)
 }
 
 static struct kb_entry *
-kb_add(void *func, ...)
+kb_add(kb_func func, ...)
 {
va_list ap;
unsigned char   ch;
-- 
2.26.2



ntpd: prevent duplicate definitions of `conf` and `ibus_dns`

2020-04-04 Thread Michael Forney
This prevents a linking error with gcc 10, which enables -fno-common
by default.

ISO C requires exactly one definition of objects with external
linkage throughout the entire program.

`conf` is already defined in ntpd.c and declared extern in ntpd.h,
so the definition in parse.y is redundant.

The two definitions of `ibuf_dns` are distinct and local to their
respective files, so make them static.
---
It looks like while the ibuf_dns variables are distinct, only one
or the other is used (one through ntp_main() and the other through
ntp_dns()). So, an alternative is to add an extern declaration in
ntpd.h.

I noticed that there are quite a few places where static could be
used, but isn't, so I'm not sure which approach is preferred.

 usr.sbin/ntpd/ntp.c | 2 +-
 usr.sbin/ntpd/ntp_dns.c | 2 +-
 usr.sbin/ntpd/parse.y   | 1 -
 3 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/usr.sbin/ntpd/ntp.c b/usr.sbin/ntpd/ntp.c
index ea9a4e92274..9cb74f1d8da 100644
--- a/usr.sbin/ntpd/ntp.c
+++ b/usr.sbin/ntpd/ntp.c
@@ -42,7 +42,7 @@
 
 volatile sig_atomic_t   ntp_quit = 0;
 struct imsgbuf *ibuf_main;
-struct imsgbuf *ibuf_dns;
+static struct imsgbuf  *ibuf_dns;
 struct ntpd_conf   *conf;
 struct ctl_connsctl_conns;
 u_int   peer_cnt;
diff --git a/usr.sbin/ntpd/ntp_dns.c b/usr.sbin/ntpd/ntp_dns.c
index 2e1a978338a..2dbd79dada6 100644
--- a/usr.sbin/ntpd/ntp_dns.c
+++ b/usr.sbin/ntpd/ntp_dns.c
@@ -39,7 +39,7 @@
 #include "ntpd.h"
 
 volatile sig_atomic_t   quit_dns = 0;
-struct imsgbuf *ibuf_dns;
+static struct imsgbuf  *ibuf_dns;
 
 void   sighdlr_dns(int);
 intdns_dispatch_imsg(struct ntpd_conf *);
diff --git a/usr.sbin/ntpd/parse.y b/usr.sbin/ntpd/parse.y
index 8d7ab09de34..533f67f1b8f 100644
--- a/usr.sbin/ntpd/parse.y
+++ b/usr.sbin/ntpd/parse.y
@@ -57,7 +57,6 @@ intlgetc(int);
 int lungetc(int);
 int findeol(void);
 
-struct ntpd_conf   *conf;
 struct sockaddr_in  query_addr4;
 struct sockaddr_in6 query_addr6;
 int poolseqnum;
-- 
2.26.0



acme-client: prevent duplicate definitions of global variables

2020-01-31 Thread Michael Forney
Every source file that includes extern.h will have its own definition
of these variables. Since many compilers allocate the variables with
.comm, they end up getting merged by the linker without error.
However, ISO C requires exactly one definition of objects with
external linkage.

gcc 10 will enable -fno-common by default, which will put
zero-initialized data in .bss, causing linking errors when multiple
definitions are present.
---
 usr.sbin/acme-client/extern.h | 4 ++--
 usr.sbin/acme-client/main.c   | 5 +++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/usr.sbin/acme-client/extern.h b/usr.sbin/acme-client/extern.h
index e6b7af0d05b..f280b3e279e 100644
--- a/usr.sbin/acme-client/extern.h
+++ b/usr.sbin/acme-client/extern.h
@@ -277,12 +277,12 @@ char  *json_fmt_signed(const char *, const 
char *, const char *);
 /*
  * Should we print debugging messages?
  */
-int verbose;
+extern int  verbose;
 
 /*
  * What component is the process within (COMP__MAX for none)?
  */
-enum comp   proccomp;
+extern enum comp proccomp;
 
 __END_DECLS
 
diff --git a/usr.sbin/acme-client/main.c b/usr.sbin/acme-client/main.c
index 7cbeeb7de03..1f59e6c755d 100644
--- a/usr.sbin/acme-client/main.c
+++ b/usr.sbin/acme-client/main.c
@@ -32,6 +32,9 @@
 #define WWW_DIR "/var/www/acme"
 #define CONF_FILE "/etc/acme-client.conf"
 
+int verbose;
+enum comp   proccomp;
+
 int
 main(int argc, char *argv[])
 {
@@ -46,8 +49,6 @@ main(int argc, char *argv[])
int   c, rc, revocate = 0;
int   popts = 0;
pid_t pids[COMP__MAX];
-   extern intverbose;
-   extern enum comp  proccomp;
size_ti, altsz, ne;
 
struct acme_conf*conf = NULL;
-- 
2.25.0



Re: Add support for hex floats to *scanf

2020-01-28 Thread Michael Forney
On 2019-05-28, Michael Forney  wrote:
> I noticed that OpenBSD's fscanf doesn't yet support hex float strings,
> which are standardized in C99. I am using them in my application (which
> I would like to support OpenBSD), since the "%a" format specifier is a
> convenient way to preserve the exact floating point value.
>
> strtod already supports parsing hex floats, so it is just the scanner
> in __svfscanf that needed changes.
>
> The implementation reuses the PFXOK and NZDIGITS flags from CT_INT
> scanning and follows similar logic to CT_INT. This required allocating
> new flag values for DPTOK and EXPOK.
>
> I did my best to follow style(9), but since the indentation level of this
> switch is so high, I found it difficult wrap lines nicely. I noticed that
> several existing lines broke the "space around binary operators" rule if
> the added space would require unnatural wrapping, so I did the same here.
>
> I wasn't sure which comments I should carry over from the CT_INT case
> (for example, above `case 'x':`), or if any of the other changes require
> additional comments. Please let me know if they do.

Just bumping this so it doesn't get forgotten. I'd be happy to address
any feedback anyone might have.



[PATCH libssl] Fix typo in man page

2019-12-02 Thread Michael Forney
---
 src/lib/libssl/man/SSL_CTX_set_cipher_list.3 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/lib/libssl/man/SSL_CTX_set_cipher_list.3 
b/src/lib/libssl/man/SSL_CTX_set_cipher_list.3
index 64da0092d..6f15dbaaa 100644
--- a/src/lib/libssl/man/SSL_CTX_set_cipher_list.3
+++ b/src/lib/libssl/man/SSL_CTX_set_cipher_list.3
@@ -230,7 +230,7 @@ An alias for
 .Cm aDSS .
 .It Cm ECDH
 An alias for
-.Cm kEECHD .
+.Cm kEECDH .
 .It Cm ECDHE
 Cipher suites using ephemeral ECDH for key exchange,
 but excluding those that don't do any server authentication.
@@ -245,7 +245,7 @@ An alias for
 .It Cm EDH
 An alias for
 .Cm DHE .
-.It Cm EECHD
+.It Cm EECDH
 An alias for
 .Cm ECDHE .
 .It Cm eNULL
-- 
2.20.1



Add support for hex floats to *scanf

2019-05-28 Thread Michael Forney
I noticed that OpenBSD's fscanf doesn't yet support hex float strings,
which are standardized in C99. I am using them in my application (which
I would like to support OpenBSD), since the "%a" format specifier is a
convenient way to preserve the exact floating point value.

strtod already supports parsing hex floats, so it is just the scanner
in __svfscanf that needed changes.

The implementation reuses the PFXOK and NZDIGITS flags from CT_INT
scanning and follows similar logic to CT_INT. This required allocating
new flag values for DPTOK and EXPOK.

I did my best to follow style(9), but since the indentation level of this
switch is so high, I found it difficult wrap lines nicely. I noticed that
several existing lines broke the "space around binary operators" rule if
the added space would require unnatural wrapping, so I did the same here.

I wasn't sure which comments I should carry over from the CT_INT case
(for example, above `case 'x':`), or if any of the other changes require
additional comments. Please let me know if they do.

diff --git lib/libc/stdio/vfscanf.c lib/libc/stdio/vfscanf.c
index 5fb55d99e61..87134a9ef86 100644
--- lib/libc/stdio/vfscanf.c
+++ lib/libc/stdio/vfscanf.c
@@ -66,19 +66,19 @@
 
 /*
  * The following are used in numeric conversions only:
- * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
- * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
+ * DPTOK and EXPOK are for floating point;
+ * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral and floating
+ * point.
  */
 #defineSIGNOK  0x01000 /* +/- is (still) legal */
 #defineHAVESIGN0x02000 /* sign detected */
 #defineNDIGITS 0x04000 /* no digits detected */
-
-#defineDPTOK   0x08000 /* (float) decimal point is still legal 
*/
-#defineEXPOK   0x1 /* (float) exponent (e+3, etc) still 
legal */
-
 #definePFXOK   0x08000 /* 0x prefix is (still) legal */
 #defineNZDIGITS0x1 /* no zero digits detected */
 
+#defineDPTOK   0x2 /* (float) decimal point is still legal 
*/
+#defineEXPOK   0x4 /* (float) exponent (e+3, etc) still 
legal */
+
 /*
  * Conversion types.
  */
@@ -770,7 +770,8 @@ literal:
width = sizeof(buf) - 2;
width++;
 #endif
-   flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
+   flags |= SIGNOK | NDIGITS | NZDIGITS | DPTOK | EXPOK;
+   base = 10;
for (p = buf; width; width--) {
c = *fp->_p;
/*
@@ -779,15 +780,36 @@ literal:
 */
switch (c) {
 
-   case '0': case '1': case '2': case '3':
+   case '0':
+   if ((flags&(NZDIGITS|NDIGITS|DPTOK)) ==
+   (NZDIGITS|NDIGITS|DPTOK))
+   flags |= PFXOK;
+   else
+   flags &= ~PFXOK;
+   flags &=
+   ~(SIGNOK | NZDIGITS | NDIGITS);
+   goto fok;
+
+   case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9':
-   flags &= ~(SIGNOK | NDIGITS);
+   flags &= ~(SIGNOK | PFXOK | NDIGITS);
+   goto fok;
+
+   /* letters ok iff hex */
+   case 'A': case 'B': case 'C':
+   case 'D': case 'F':
+   case 'a': case 'b': case 'c':
+   case 'd': case 'f':
+   if (base == 10)
+   break;  /* not legal here */
+   flags &= ~(SIGNOK | PFXOK | NDIGITS);
goto fok;
 
case '+': case '-':
if (flags & SIGNOK) {
flags &= ~SIGNOK;
+   flags |= HAVESIGN;
goto fok;
}
break;
@@ -799,11 +821,35 @@ literal:
break;
case 'e': case 'E':
/* no exponent without some digits */
- 

Re: doas: add fd variable, use fd instead of i, to main()

2017-03-09 Thread Michael Forney
On Sun, Nov 27, 2016 at 12:31 AM, Hajime Edakawa
 wrote:
> Dear tech,
>
> I guessed it more better to use fd instead of i.
>
> Would this be OK?
>
> Sincerely, tech
> Edakawa
>
> Index: doas.c
> ===
> RCS file: /cvs/src/usr.bin/doas/doas.c,v
> retrieving revision 1.68
> diff -u -p -u -r1.68 doas.c
> --- doas.c  5 Oct 2016 23:28:28 -   1.68
> +++ doas.c  27 Nov 2016 08:20:45 -
> @@ -256,7 +256,7 @@ main(int argc, char **argv)
> uid_t target = 0;
> gid_t groups[NGROUPS_MAX + 1];
> int ngroups;
> -   int i, ch;
> +   int i, ch, fd;
> int sflag = 0;
> int nflag = 0;
> char cwdpath[PATH_MAX];
> @@ -279,10 +279,10 @@ main(int argc, char **argv)
> confpath = optarg;
> break;
> case 'L':
> -   i = open("/dev/tty", O_RDWR);
> -   if (i != -1)
> -   ioctl(i, TIOCCLRVERAUTH);
> -   exit(i != -1);
> +   fd = open("/dev/tty", O_RDWR);
> +   if (fd != -1)
> +   ioctl(fd, TIOCCLRVERAUTH);
> +   exit(fd != -1);

While you're at it, I think this exit status is backwards. Currently
`doas -L` exits 0 if the open failed.

> case 'u':
> if (parseuid(optarg, ) != 0)
> errx(1, "unknown user");
>