libbsd: Branch 'master' - 17 commits

Guillem Jover guillem at kemper.freedesktop.org
Mon May 21 02:56:56 UTC 2018


 COPYING                  |   28 +
 configure.ac             |    4 
 include/Makefile.am      |    1 
 include/bsd/inttypes.h   |   49 ++
 include/bsd/libutil.h    |   26 -
 include/bsd/sys/cdefs.h  |    6 
 include/bsd/vis.h        |   57 ++-
 man/Makefile.am          |    2 
 man/flopen.3bsd          |   42 +-
 man/fmtcheck.3bsd        |   61 +--
 man/libbsd.7             |   18 
 man/pidfile.3bsd         |   77 +++-
 man/readpassphrase.3bsd  |   16 
 man/strtoi.3bsd          |  238 +++++++++++++
 man/strtonum.3bsd        |   83 +++-
 man/strtou.3bsd          |  238 +++++++++++++
 man/unvis.3bsd           |  201 +++++++----
 man/vis.3bsd             |  440 ++++++++++++++++++------
 src/Makefile.am          |    7 
 src/arc4random.h         |    4 
 src/arc4random_linux.h   |   88 ++++
 src/arc4random_unix.h    |    3 
 src/arc4random_win.h     |   78 ++++
 src/chacha_private.h     |    2 
 src/fgetln.c             |    2 
 src/fgetwln.c            |    5 
 src/flopen.c             |   62 ++-
 src/fmtcheck.c           |  174 +++++++--
 src/getentropy.c         |    2 
 src/getentropy_aix.c     |    4 
 src/getentropy_bsd.c     |    4 
 src/getentropy_hpux.c    |    2 
 src/getentropy_hurd.c    |    4 
 src/getentropy_linux.c   |   25 -
 src/getentropy_osx.c     |   15 
 src/getentropy_solaris.c |    4 
 src/getentropy_win.c     |   59 +++
 src/heapsort.c           |    9 
 src/humanize_number.c    |  133 ++++---
 src/libbsd.map           |   21 +
 src/pidfile.c            |   58 ++-
 src/radixsort.c          |    3 
 src/readpassphrase.c     |  116 +++---
 src/strtoi.c             |   97 +++++
 src/strtonum.c           |   95 ++---
 src/strtou.c             |   97 +++++
 src/unvis.c              |  538 ++++++++++++++++++++---------
 src/vis.c                |  856 +++++++++++++++++++++++++++++++++++++----------
 48 files changed, 3261 insertions(+), 893 deletions(-)

New commits:
commit e007233cf0ea62bbeab30f960083006cb676f973
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 04:41:56 2018 +0200

    Release libbsd 0.9.0

diff --git a/configure.ac b/configure.ac
index 88ccd91..598583a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,8 +12,8 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])],
                             [AC_SUBST([AM_DEFAULT_VERBOSITY], [1])])
 
 LIBBSD_ABI_MAJOR=0
-LIBBSD_ABI_MINOR=8
-LIBBSD_ABI_PATCH=7
+LIBBSD_ABI_MINOR=9
+LIBBSD_ABI_PATCH=0
 
 LIBBSD_ABI="$LIBBSD_ABI_MAJOR:$LIBBSD_ABI_MINOR:$LIBBSD_ABI_PATCH"
 AC_SUBST([LIBBSD_ABI])
commit 3cabf46bb089bbb5924c659941b9c8cb67ce3508
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 03:15:59 2018 +0200

    Deprecate fgetwln()
    
    This function has the same problems as fgetln() which is already marked
    as deprecated.

diff --git a/man/libbsd.7 b/man/libbsd.7
index 0521f93..7d58c19 100644
--- a/man/libbsd.7
+++ b/man/libbsd.7
@@ -1,6 +1,6 @@
 .\" libbsd man page
 .\"
-.\" Copyright © 2017 Gullem Jover <guillem at hadrons.org>
+.\" Copyright © 2017-2018 Gullem Jover <guillem at hadrons.org>
 .\"
 .\" Redistribution and use in source and binary forms, with or without
 .\" modification, are permitted provided that the following conditions
@@ -24,7 +24,7 @@
 .\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd May 31 2017
+.Dd May 21 2018
 .Dt LIBBSD 7
 .Os
 .Sh NAME
@@ -143,6 +143,17 @@ Use
 .Fn getline 3
 instead, which is available in many systems and required by
 .St -p1003.1-2008 .
+.It Fn fgetwln
+Unportable, requires assistance from the stdio layer.
+An implementation has to choose between leaking buffers or being reentrant
+for a limited amount of streams (this implementation chose the latter with
+a limit of 32).
+Use
+.Fn fgetwc 3
+instead, which is available in many systems and required by
+.St -isoC-99
+and
+.St -p1003.1-2001 .
 .It Fn funopen
 Unportable, requires assistance from the stdio layer or some hook framework.
 On GNU systems the
diff --git a/src/fgetwln.c b/src/fgetwln.c
index aa3f927..1127655 100644
--- a/src/fgetwln.c
+++ b/src/fgetwln.c
@@ -30,6 +30,8 @@
 #include <stdio.h>
 #include <wchar.h>
 
+#include "local-link.h"
+
 struct filewbuf {
 	FILE *fp;
 	wchar_t *wbuf;
@@ -85,3 +87,6 @@ fgetwln(FILE *stream, size_t *lenp)
 	*lenp = wused;
 	return wused ? fb->wbuf : NULL;
 }
+libbsd_link_warning(fgetwln,
+                    "This function cannot be safely ported, use fgetwc(3) "
+                    "instead, as it is supported by C99 and POSIX.1-2001.")
commit 6eebc1f2645f0c77508b5c536bac6ed81bd2502b
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 03:10:12 2018 +0200

    Fix typo in fgetln() linker warning

diff --git a/src/fgetln.c b/src/fgetln.c
index 4d1726e..7461a7a 100644
--- a/src/fgetln.c
+++ b/src/fgetln.c
@@ -76,7 +76,7 @@ fgetln(FILE *stream, size_t *len)
 	}
 }
 libbsd_link_warning(fgetln,
-                    "This functions cannot be safely ported, use getline(3) "
+                    "This function cannot be safely ported, use getline(3) "
                     "instead, as it is supported by GNU and POSIX.1-2008.")
 #else
 #error "Function fgetln() needs to be ported."
commit a1730c10632b9e1e800db5e280eea576bc9052ec
Author: Guillem Jover <guillem at hadrons.org>
Date:   Tue May 15 00:55:02 2018 +0200

    Add Windows support for getentropy() and arc4random()
    
    Import from OpenBSD.

diff --git a/COPYING b/COPYING
index e197193..26df85f 100644
--- a/COPYING
+++ b/COPYING
@@ -449,6 +449,7 @@ Files:
  src/arc4random_openbsd.h
  src/arc4random_uniform.c
  src/arc4random_unix.h
+ src/arc4random_win.h
  src/closefrom.c
  src/getentropy_aix.c
  src/getentropy_bsd.c
@@ -457,6 +458,7 @@ Files:
  src/getentropy_linux.c
  src/getentropy_osx.c
  src/getentropy_solaris.c
+ src/getentropy_win.c
  src/readpassphrase.c
  src/reallocarray.c
  src/strlcat.c
diff --git a/src/Makefile.am b/src/Makefile.am
index d85dc69..f3cc0fa 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,6 +16,7 @@ libbsd_la_included_sources = \
 	getentropy_linux.c \
 	getentropy_osx.c \
 	getentropy_solaris.c \
+	getentropy_win.c \
 	$(nil)
 
 EXTRA_DIST = \
@@ -64,6 +65,7 @@ libbsd_la_SOURCES = \
 	arc4random_openbsd.h \
 	arc4random_uniform.c \
 	arc4random_unix.h \
+	arc4random_win.h \
 	bsd_getopt.c \
 	chacha_private.h \
 	closefrom.c \
diff --git a/src/arc4random.h b/src/arc4random.h
index 803ef86..812188b 100644
--- a/src/arc4random.h
+++ b/src/arc4random.h
@@ -36,6 +36,8 @@ getentropy(void *buf, size_t len);
 #include "arc4random_openbsd.h"
 #elif defined(__linux__)
 #include "arc4random_linux.h"
+#elif defined(_WIN32)
+#include "arc4random_win.h"
 #else
 #include "arc4random_unix.h"
 #endif
diff --git a/src/arc4random_win.h b/src/arc4random_win.h
new file mode 100644
index 0000000..deec8a1
--- /dev/null
+++ b/src/arc4random_win.h
@@ -0,0 +1,78 @@
+/*	$OpenBSD: arc4random_win.h,v 1.6 2016/06/30 12:17:29 bcook Exp $	*/
+
+/*
+ * Copyright (c) 1996, David Mazieres <dm at uun.org>
+ * Copyright (c) 2008, Damien Miller <djm at openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus at openbsd.org>
+ * Copyright (c) 2014, Theo de Raadt <deraadt at openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Stub functions for portability.
+ */
+
+#include <windows.h>
+
+static volatile HANDLE arc4random_mtx = NULL;
+
+/*
+ * Initialize the mutex on the first lock attempt. On collision, each thread
+ * will attempt to allocate a mutex and compare-and-swap it into place as the
+ * global mutex. On failure to swap in the global mutex, the mutex is closed.
+ */
+#define _ARC4_LOCK() { \
+	if (!arc4random_mtx) { \
+		HANDLE p = CreateMutex(NULL, FALSE, NULL); \
+		if (InterlockedCompareExchangePointer((void **)&arc4random_mtx, (void *)p, NULL)) \
+			CloseHandle(p); \
+	} \
+	WaitForSingleObject(arc4random_mtx, INFINITE); \
+} \
+
+#define _ARC4_UNLOCK() ReleaseMutex(arc4random_mtx)
+
+static inline void
+_getentropy_fail(void)
+{
+	TerminateProcess(GetCurrentProcess(), 0);
+}
+
+static inline int
+_rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
+{
+	*rsp = VirtualAlloc(NULL, sizeof(**rsp),
+	    MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+	if (*rsp == NULL)
+		return (-1);
+
+	*rsxp = VirtualAlloc(NULL, sizeof(**rsxp),
+	    MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+	if (*rsxp == NULL) {
+		VirtualFree(*rsp, 0, MEM_RELEASE);
+		*rsp = NULL;
+		return (-1);
+	}
+	return (0);
+}
+
+static inline void
+_rs_forkhandler(void)
+{
+}
+
+static inline void
+_rs_forkdetect(void)
+{
+}
diff --git a/src/getentropy.c b/src/getentropy.c
index 3f11a1e..b4b3fe3 100644
--- a/src/getentropy.c
+++ b/src/getentropy.c
@@ -40,6 +40,8 @@
 #include "getentropy_aix.c"
 #elif defined(__hpux)
 #include "getentropy_hpux.c"
+#elif defined(_WIN32)
+#include "getentropy_win.c"
 #else
 #error "No getentropy hooks defined for this platform."
 #endif
diff --git a/src/getentropy_win.c b/src/getentropy_win.c
new file mode 100644
index 0000000..bc548e6
--- /dev/null
+++ b/src/getentropy_win.c
@@ -0,0 +1,59 @@
+/*	$OpenBSD: getentropy_win.c,v 1.5 2016/08/07 03:27:21 tb Exp $	*/
+
+/*
+ * Copyright (c) 2014, Theo de Raadt <deraadt at openbsd.org>
+ * Copyright (c) 2014, Bob Beck <beck at obtuse.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Emulation of getentropy(2) as documented at:
+ * http://man.openbsd.org/getentropy.2
+ */
+
+#include <windows.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <wincrypt.h>
+#include <process.h>
+
+int	getentropy(void *buf, size_t len);
+
+/*
+ * On Windows, CryptGenRandom is supposed to be a well-seeded
+ * cryptographically strong random number generator.
+ */
+int
+getentropy(void *buf, size_t len)
+{
+	HCRYPTPROV provider;
+
+	if (len > 256) {
+		errno = EIO;
+		return (-1);
+	}
+
+	if (CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
+	    CRYPT_VERIFYCONTEXT) == 0)
+		goto fail;
+	if (CryptGenRandom(provider, len, buf) == 0) {
+		CryptReleaseContext(provider, 0);
+		goto fail;
+	}
+	CryptReleaseContext(provider, 0);
+	return (0);
+
+fail:
+	errno = EIO;
+	return (-1);
+}
commit 6f68c930765d95ab2644c7a1210a0a640fd9d4bf
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 00:22:38 2018 +0200

    Switch strtonum() implementation from strtoll() to strtoi()
    
    Import from NetBSD.

diff --git a/COPYING b/COPYING
index 10fb229..e197193 100644
--- a/COPYING
+++ b/COPYING
@@ -274,8 +274,10 @@ Files:
  src/fmtcheck.c
  src/humanize_number.c
  src/stringlist.c
+ src/strtonum.c
 Copyright:
- Copyright © 1994, 1997-2000, 2002, 2008, 2010 The NetBSD Foundation, Inc.
+ Copyright © 1994, 1997-2000, 2002, 2008, 2010, 2014
+     The NetBSD Foundation, Inc.
  Copyright © 2013 John-Mark Gurney <jmg at FreeBSD.org>
  All rights reserved.
  .
@@ -459,7 +461,6 @@ Files:
  src/reallocarray.c
  src/strlcat.c
  src/strlcpy.c
- src/strtonum.c
 Copyright:
  Copyright © 2004 Ted Unangst and Todd Miller
  All rights reserved.
diff --git a/man/strtonum.3bsd b/man/strtonum.3bsd
index fd34ad6..7d82fc8 100644
--- a/man/strtonum.3bsd
+++ b/man/strtonum.3bsd
@@ -1,3 +1,6 @@
+.\" $NetBSD: strtonum.3,v 1.2 2015/01/19 11:47:41 wiz Exp $
+.\" $OpenBSD: strtonum.3,v 1.17 2013/08/14 06:32:28 jmc Exp $
+.\"
 .\" Copyright (c) 2004 Ted Unangst
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
@@ -12,10 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.\" $OpenBSD: strtonum.3,v 1.12 2005/10/26 11:37:58 jmc Exp $
-.\" $FreeBSD$
-.\"
-.Dd April 29, 2004
+.Dd January 18, 2015
 .Dt STRTONUM 3bsd
 .Os
 .Sh NAME
@@ -45,14 +45,6 @@ function converts the string in
 to a
 .Vt "long long"
 value.
-The
-.Fn strtonum
-function was designed to facilitate safe, robust programming
-and overcome the shortcomings of the
-.Xr atoi 3
-and
-.Xr strtol 3
-family of interfaces.
 .Pp
 The string may begin with an arbitrary amount of whitespace
 (as determined by
@@ -112,15 +104,13 @@ The above example will guarantee that the value of iterations is between
 1 and 64 (inclusive).
 .Sh ERRORS
 .Bl -tag -width Er
-.It Bq Er ERANGE
-The given string was out of range.
 .It Bq Er EINVAL
-The given string did not consist solely of digit characters.
-.It Bq Er EINVAL
-The supplied
-.Fa minval
+The given string did not consist solely of digit characters; or
+.Ar minval
 was larger than
-.Fa maxval .
+.Ar maxval .
+.It Bq Er ERANGE
+The given string was out of range.
 .El
 .Pp
 If an error occurs,
@@ -142,21 +132,58 @@ The string did not consist solely of digit characters.
 .Xr atoll 3 ,
 .Xr sscanf 3 ,
 .Xr strtod 3 ,
+.Xr strtoi 3bsd ,
 .Xr strtol 3 ,
-.Xr strtoul 3
+.Xr strtoll 3 ,
+.Xr strtou 3bsd ,
+.Xr strtoul 3 ,
+.Xr strtoull 3
 .Sh STANDARDS
-The
 .Fn strtonum
-function is a
-.Bx
+is an
+.Ox
 extension.
-The existing alternatives, such as
-.Xr atoi 3
-and
-.Xr strtol 3 ,
-are either impossible or difficult to use safely.
 .Sh HISTORY
 The
 .Fn strtonum
 function first appeared in
 .Ox 3.6 .
+.Fn strtonum
+was redesigned in
+.Nx 8
+as
+.Fn strtoi 3bsd
+and
+.Fn strtou 3bsd .
+.Sh CAVEATS
+The
+.Fn strtonum
+function was designed to facilitate safe,
+robust programming and overcome the shortcomings of the
+.Xr atoi 3
+and
+.Xr strtol 3
+family of interfaces, however there are problems with the
+.Fn strtonum
+API:
+.Bl -dash
+.It
+will return 0 on failure; 0 might not be in range, so that necessitates
+an error check even if you want to avoid it
+.It
+does not differentiate 'illegal' returns, so we can't tell the
+difference between partial and no conversions
+.It
+returns english strings
+.It
+can't set the base, or find where the conversion ended
+.It
+hardcodes long long integer type
+.El
+To overcome the shortcomings of
+.Fn strtonum
+.Nx
+provides
+.Fn strtou 3bsd
+and
+.Fn strtoi 3bsd .
diff --git a/src/strtonum.c b/src/strtonum.c
index 1c722ab..2fa0fcf 100644
--- a/src/strtonum.c
+++ b/src/strtonum.c
@@ -1,67 +1,62 @@
+/*	$NetBSD: strtonum.c,v 1.5 2018/01/04 20:57:29 kamil Exp $	*/
 /*-
- * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * Copyright (c) 2014 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
  *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
  *
- *	$OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include <sys/cdefs.h>
 
-#include <errno.h>
-#include <limits.h>
+#include <stdio.h>
 #include <stdlib.h>
-
-#define INVALID 	1
-#define TOOSMALL 	2
-#define TOOLARGE 	3
+#include <errno.h>
+#include <inttypes.h>
 
 long long
-strtonum(const char *numstr, long long minval, long long maxval,
-    const char **errstrp)
+strtonum(const char *nptr, long long minval, long long maxval,
+         const char **errstr)
 {
-	long long ll = 0;
-	char *ep;
-	int error = 0;
-	struct errval {
-		const char *errstr;
-		int err;
-	} ev[4] = {
-		{ NULL,		0 },
-		{ "invalid",	EINVAL },
-		{ "too small",	ERANGE },
-		{ "too large",	ERANGE },
-	};
+	int e;
+	long long rv;
+	const char *resp;
 
-	ev[0].err = errno;
-	errno = 0;
-	if (minval > maxval)
-		error = INVALID;
-	else {
-		ll = strtoll(numstr, &ep, 10);
-		if (errno == EINVAL || numstr == ep || *ep != '\0')
-			error = INVALID;
-		else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
-			error = TOOSMALL;
-		else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
-			error = TOOLARGE;
+	if (errstr == NULL)
+		errstr = &resp;
+
+	rv = (long long)strtoi(nptr, NULL, 10, minval, maxval, &e);
+
+	if (e == 0) {
+		*errstr = NULL;
+		return rv;
 	}
-	if (errstrp != NULL)
-		*errstrp = ev[error].errstr;
-	errno = ev[error].err;
-	if (error)
-		ll = 0;
 
-	return (ll);
+	if (e == ERANGE)
+		*errstr = (rv == maxval ? "too large" : "too small");
+	else
+		*errstr = "invalid";
+
+	return 0;
 }
commit e13b1a337a608c3301ccb9ea344b6f97279a818f
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 00:20:49 2018 +0200

    Import strtoi() and strtou() functions from NetBSD

diff --git a/COPYING b/COPYING
index 4377959..10fb229 100644
--- a/COPYING
+++ b/COPYING
@@ -3,7 +3,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Files:
  *
 Copyright:
- Copyright © 2004-2006, 2008-2017 Guillem Jover <guillem at hadrons.org>
+ Copyright © 2004-2006, 2008-2018 Guillem Jover <guillem at hadrons.org>
 License: BSD-3-clause
 
 Files:
@@ -110,6 +110,8 @@ Files:
  man/setmode.3bsd
  man/strmode.3bsd
  man/strnstr.3bsd
+ man/strtoi.3bsd
+ man/strtou.3bsd
  man/unvis.3bsd
  man/vis.3bsd
  man/wcslcpy.3bsd
@@ -121,6 +123,8 @@ Files:
  src/setmode.c
  src/strmode.c
  src/strnstr.c
+ src/strtoi.c
+ src/strtou.c
  src/unvis.c
 Copyright:
  Copyright © 1980, 1982, 1986, 1989-1994
diff --git a/include/Makefile.am b/include/Makefile.am
index 6c74b44..ce3f058 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -13,6 +13,7 @@ nobase_include_HEADERS = \
 	bsd/bsd.h \
 	bsd/err.h \
 	bsd/getopt.h \
+	bsd/inttypes.h \
 	bsd/libutil.h \
 	bsd/md5.h \
 	bsd/nlist.h \
diff --git a/include/bsd/inttypes.h b/include/bsd/inttypes.h
new file mode 100644
index 0000000..a2b89f7
--- /dev/null
+++ b/include/bsd/inttypes.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2018 Guillem Jover <guillem at hadrons.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef LIBBSD_OVERLAY
+#include_next <inttypes.h>
+#else
+#include <inttypes.h>
+#endif
+
+#ifndef LIBBSD_INTTYPES_H
+#define LIBBSD_INTTYPES_H
+
+#ifdef LIBBSD_OVERLAY
+#include <sys/cdefs.h>
+#else
+#include <bsd/sys/cdefs.h>
+#endif
+
+__BEGIN_DECLS
+intmax_t strtoi(const char *__restrict nptr, char **__restrict endptr,
+                int base, intmax_t lo, intmax_t hi, int *rstatus);
+uintmax_t strtou(const char *__restrict nptr, char **__restrict endptr,
+                 int base, uintmax_t lo, uintmax_t hi, int *rstatus);
+__END_DECLS
+
+#endif
diff --git a/man/Makefile.am b/man/Makefile.am
index 28192c0..26e893a 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -212,7 +212,9 @@ dist_man_MANS = \
 	strnstr.3bsd \
 	strnunvis.3bsd \
 	strnvis.3bsd \
+	strtoi.3bsd \
 	strtonum.3bsd \
+	strtou.3bsd \
 	strunvis.3bsd \
 	strvis.3bsd \
 	strvisx.3bsd \
diff --git a/man/libbsd.7 b/man/libbsd.7
index ea2d281..0521f93 100644
--- a/man/libbsd.7
+++ b/man/libbsd.7
@@ -94,6 +94,7 @@ be prefixed with
 .It In bitstring.h
 .It In err.h
 .It In getopt.h
+.It In inttypes.h
 .It In libutil.h
 .It In md5.h
 .It In netinet/ip_icmp.h
@@ -214,7 +215,9 @@ This function is provided by
 .Xr strlcpy 3bsd ,
 .Xr strmode 3bsd ,
 .Xr strnstr 3bsd ,
+.Xr strtoi 3bsd ,
 .Xr strtonum 3bsd ,
+.Xr strtou 3bsd ,
 .Xr timeradd 3bsd ,
 .Xr timeval 3bsd ,
 .Xr tree 3bsd ,
diff --git a/man/strtoi.3bsd b/man/strtoi.3bsd
new file mode 100644
index 0000000..273d336
--- /dev/null
+++ b/man/strtoi.3bsd
@@ -0,0 +1,238 @@
+.\"	$NetBSD: strtoi.3,v 1.7 2017/07/03 21:32:50 wiz Exp $
+.\"
+.\" Copyright (c) 1990, 1991, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Chris Torek and the American National Standards Committee X3,
+.\" on Information Processing Systems.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     from: @(#)strtol.3	8.1 (Berkeley) 6/4/93
+.\"
+.\" Created by Kamil Rytarowski, based on ID:
+.\" NetBSD: strtol.3,v 1.31 2015/03/11 09:57:35 wiz Exp
+.\"
+.Dd November 13, 2015
+.Dt STRTOI 3bsd
+.Os
+.Sh NAME
+.Nm strtoi
+.Nd convert string value to an intmax_t integer
+.Sh LIBRARY
+.ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
+.Lb libbsd
+.Sh SYNOPSIS
+.In inttypes.h
+(See
+.Xr libbsd 7
+for include usage.)
+.Ft intmax_t
+.Fo strtoi
+.Fa "const char * restrict nptr"
+.Fa "char ** restrict endptr"
+.Fa "int base"
+.Fa "intmax_t lo"
+.Fa "intmax_t hi"
+.Fa "int *rstatus"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn strtoi
+function
+converts the string in
+.Fa nptr
+to an
+.Ft intmax_t
+value.
+The
+.Fn strtoi
+function uses internally
+.Xr strtoimax 3
+and ensures that the result is always in the range [
+.Fa lo ..
+.Fa hi
+].
+In adddition it always places
+.Dv 0
+on success or a conversion status in the
+.Fa rstatus
+argument, avoiding the
+.Dv errno
+gymnastics the other functions require.
+The
+.Fa rstatus
+argument can be
+.Dv NULL
+if conversion status is to be ignored.
+.Pp
+The string may begin with an arbitrary amount of white space
+(as determined by
+.Xr isspace 3 )
+followed by a single optional
+.Ql +
+or
+.Ql -
+sign.
+If
+.Fa base
+is zero or 16,
+the string may then include a
+.Ql 0x
+or
+.Ql 0X
+prefix,
+and the number will be read in base 16; otherwise,
+.\" if the
+.\" .Fa base
+.\" is zero or 2,
+.\" the string may then include a
+.\" .Ql 0b
+.\" or
+.\" .Ql 0B
+.\" prefix,
+.\" and the number will be read in base 2; otherwise,
+a zero
+.Fa base
+is taken as 10 (decimal) unless the next character is
+.Ql 0 ,
+in which case it is taken as 8 (octal).
+.Pp
+The remainder of the string is converted to a
+.Em intmax_t
+value in the obvious manner,
+stopping at the first character which is not a valid digit
+in the given base.
+(In bases above 10, the letter
+.Ql A
+in either upper or lower case
+represents 10,
+.Ql B
+represents 11, and so forth, with
+.Ql Z
+representing 35.)
+.Pp
+If
+.Fa endptr
+is non-nil,
+.Fn strtoi
+stores the address of the first invalid character in
+.Fa *endptr .
+If there were no digits at all, however,
+.Fn strtoi
+stores the original value of
+.Fa nptr
+in
+.Fa *endptr .
+(Thus, if
+.Fa *nptr
+is not
+.Ql \e0
+but
+.Fa **endptr
+is
+.Ql \e0
+on return, the entire string was valid.)
+.Sh RETURN VALUES
+The
+.Fn strtoi
+function
+always returns the closest value in the range specified by
+the
+.Fa lo
+and
+.Fa hi
+arguments.
+.Pp
+The
+.Va errno
+value is guaranteed to be left unchanged.
+.Pp
+Errors are stored as the conversion status in the
+.Fa rstatus
+argument.
+.Sh EXAMPLES
+The following example will always return a number in
+.Dv [1..99]
+range no matter what the input is, and warn if the conversion failed.
+.Bd -literal -offset indent
+int e;
+intmax_t lval = strtoi(buf, NULL, 0, 1, 99, &e);
+if (e)
+	warnc(e, "conversion of `%s' to a number failed, using %jd",
+	    buf, lval);
+.Ed
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er ECANCELED
+The string did not contain any characters that were converted.
+.It Bq Er EINVAL
+The
+.Ar base
+is not between 2 and 36 and does not contain the special value 0.
+.It Bq Er ENOTSUP
+The string contained non-numeric characters that did not get converted.
+In this case,
+.Fa endptr
+points to the first unconverted character.
+.It Bq Er ERANGE
+The given string was out of range; the value converted has been clamped;
+or the range given was invalid, i.e.
+.Fa lo
+>
+.Fa hi .
+.El
+.Sh SEE ALSO
+.Xr atof 3 ,
+.Xr atoi 3 ,
+.Xr atol 3 ,
+.Xr atoll 3 ,
+.Xr strtod 3 ,
+.Xr strtoimax 3 ,
+.Xr strtol 3 ,
+.Xr strtoll 3 ,
+.Xr strtou 3bsd ,
+.Xr strtoul 3 ,
+.Xr strtoull 3 ,
+.Xr strtoumax 3
+.Sh STANDARDS
+The
+.Fn strtoi
+function is a
+.Nx
+extension.
+.Sh HISTORY
+The
+.Fn strtoi
+function first appeared in
+.Nx 7 .
+.Ox
+introduced the
+.Fn strtonum 3bsd
+function for the same purpose, but the interface makes it impossible to
+properly differentiate illegal returns.
+.Sh BUGS
+Ignores the current locale.
diff --git a/man/strtou.3bsd b/man/strtou.3bsd
new file mode 100644
index 0000000..fc0f901
--- /dev/null
+++ b/man/strtou.3bsd
@@ -0,0 +1,238 @@
+.\"	$NetBSD: strtou.3,v 1.7 2017/07/03 21:32:50 wiz Exp $
+.\"
+.\" Copyright (c) 1990, 1991, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Chris Torek and the American National Standards Committee X3,
+.\" on Information Processing Systems.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     from: @(#)strtoul.3	8.1 (Berkeley) 6/4/93
+.\"
+.\" Created by Kamil Rytarowski, based on ID:
+.\" NetBSD: strtoul.3,v 1.29 2015/03/10 13:00:58 christos Exp
+.\"
+.Dd November 13, 2015
+.Dt STRTOU 3bsd
+.Os
+.Sh NAME
+.Nm strtou
+.Nd convert a string to an uintmax_t integer
+.Sh LIBRARY
+.ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
+.Lb libbsd
+.Sh SYNOPSIS
+.In inttypes.h
+(See
+.Xr libbsd 7
+for include usage.)
+.Ft uintmax_t
+.Fo strtou
+.Fa "const char * restrict nptr"
+.Fa "char ** restrict endptr"
+.Fa "int base"
+.Fa "uintmax_t lo"
+.Fa "uintmax_t hi"
+.Fa "int *rstatus"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn strtou
+function converts the string in
+.Fa nptr
+to an
+.Ft uintmax_t
+value.
+The
+.Fn strtou
+function uses internally
+.Xr strtoumax 3
+and ensures that the result is always in the range [
+.Fa lo ..
+.Fa hi
+].
+In adddition it always places
+.Dv 0
+on success or a conversion status in the
+.Fa rstatus
+argument, avoiding the
+.Dv errno
+gymnastics the other functions require.
+The
+.Fa rstatus
+argument can be
+.Dv NULL
+if conversion status is to be ignored.
+.Pp
+The string may begin with an arbitrary amount of white space
+(as determined by
+.Xr isspace 3 )
+followed by a single optional
+.Ql +
+or
+.Ql -
+sign.
+If
+.Fa base
+is zero or 16,
+the string may then include a
+.Ql 0x
+or
+.Ql 0X
+prefix,
+and the number will be read in base 16; otherwise,
+.\" if the
+.\" .Fa base
+.\" is zero or 2,
+.\" the string may then include a
+.\" .Ql 0b
+.\" or
+.\" .Ql 0B
+.\" prefix,
+.\" and the number will be read in base 2; otherwise,
+a zero
+.Fa base
+is taken as 10 (decimal) unless the next character is
+.Ql 0 ,
+in which case it is taken as 8 (octal).
+.Pp
+The remainder of the string is converted to an
+.Em uintmax_t
+value in the obvious manner,
+stopping at the end of the string
+or at the first character that does not produce a valid digit
+in the given base.
+(In bases above 10, the letter
+.Ql A
+in either upper or lower case
+represents 10,
+.Ql B
+represents 11, and so forth, with
+.Ql Z
+representing 35.)
+.Pp
+If
+.Fa endptr
+is non-nil,
+.Fn strtou
+stores the address of the first invalid character in
+.Fa *endptr .
+If there were no digits at all, however,
+.Fn strtou
+stores the original value of
+.Fa nptr
+in
+.Fa *endptr .
+(Thus, if
+.Fa *nptr
+is not
+.Ql \e0
+but
+.Fa **endptr
+is
+.Ql \e0
+on return, the entire string was valid.)
+.Sh RETURN VALUES
+The
+.Fn strtou
+function
+always returns the closest value in the range specified by
+the
+.Fa lo
+and
+.Fa hi
+arguments.
+.Pp
+The
+.Va errno
+value is guaranteed to be left unchanged.
+.Pp
+Errors are stored as the conversion status in the
+.Fa rstatus
+argument.
+.Sh EXAMPLES
+The following example will always return a number in
+.Dv [1..99]
+range no matter what the input is, and warn if the conversion failed.
+.Bd -literal -offset indent
+int e;
+uintmax_t lval = strtou(buf, NULL, 0, 1, 99, &e);
+if (e)
+	warnc(e, "conversion of `%s' to a number failed, using %ju",
+	    buf, lval);
+.Ed
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er ECANCELED
+The string did not contain any characters that were converted.
+.It Bq Er EINVAL
+The
+.Ar base
+is not between 2 and 36 and does not contain the special value 0.
+.It Bq Er ENOTSUP
+The string contained non-numeric characters that did not get converted.
+In this case,
+.Fa endptr
+points to the first unconverted character.
+.It Bq Er ERANGE
+The given string was out of range; the value converted has been clamped; or
+the range given was invalid, i.e.
+.Fa lo
+>
+.Fa hi .
+.El
+.Sh SEE ALSO
+.Xr atof 3 ,
+.Xr atoi 3 ,
+.Xr atol 3 ,
+.Xr atoll 3 ,
+.Xr strtod 3 ,
+.Xr strtoi 3bsd ,
+.Xr strtoimax 3 ,
+.Xr strtol 3 ,
+.Xr strtoll 3 ,
+.Xr strtoul 3 ,
+.Xr strtoull 3 ,
+.Xr strtoumax 3
+.Sh STANDARDS
+The
+.Fn strtou
+function is a
+.Nx
+extension.
+.Sh HISTORY
+The
+.Fn strtou
+function first appeared in
+.Nx 7 .
+.Ox
+introduced the
+.Fn strtonum 3bsd
+function for the same purpose, but the interface makes it impossible to
+properly differentiate illegal returns.
+.Sh BUGS
+Ignores the current locale.
diff --git a/src/Makefile.am b/src/Makefile.am
index d72802e..d85dc69 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -104,7 +104,9 @@ libbsd_la_SOURCES = \
 	stringlist.c \
 	strmode.c \
 	strnstr.c \
+	strtoi.c \
 	strtonum.c \
+	strtou.c \
 	timeconv.c \
 	unvis.c \
 	vis.c \
diff --git a/src/libbsd.map b/src/libbsd.map
index a91d522..98b95ce 100644
--- a/src/libbsd.map
+++ b/src/libbsd.map
@@ -145,6 +145,9 @@ LIBBSD_0.9 {
 
     pidfile_fileno;
 
+    strtoi;
+    strtou;
+
     nvis;
     snvis;
     stravis;
diff --git a/src/strtoi.c b/src/strtoi.c
new file mode 100644
index 0000000..9e3771d
--- /dev/null
+++ b/src/strtoi.c
@@ -0,0 +1,97 @@
+/*	$NetBSD: _strtoi.h,v 1.2 2015/01/18 17:55:22 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Original version ID:
+ * NetBSD: src/lib/libc/locale/_wcstoul.h,v 1.2 2003/08/07 16:43:03 agc Exp
+ *
+ * Created by Kamil Rytarowski, based on ID:
+ * NetBSD: src/common/lib/libc/stdlib/_strtoul.h,v 1.7 2013/05/17 12:55:56 joe…
+ */
+
+#include <sys/cdefs.h>
+
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#define _DIAGASSERT(t)
+
+intmax_t
+strtoi(const char *__restrict nptr,
+       char **__restrict endptr, int base,
+       intmax_t lo, intmax_t hi, int *rstatus)
+{
+	int serrno;
+	intmax_t im;
+	char *ep;
+	int rep;
+
+	_DIAGASSERT(hi >= lo);
+
+	_DIAGASSERT(nptr != NULL);
+	/* endptr may be NULL */
+
+	if (endptr == NULL)
+		endptr = &ep;
+
+	if (rstatus == NULL)
+		rstatus = &rep;
+
+	serrno = errno;
+	errno = 0;
+
+	im = strtoimax(nptr, endptr, base);
+
+	*rstatus = errno;
+	errno = serrno;
+
+	if (*rstatus == 0) {
+		/* No digits were found */
+		if (nptr == *endptr)
+			*rstatus = ECANCELED;
+		/* There are further characters after number */
+		else if (**endptr != '\0')
+			*rstatus = ENOTSUP;
+	}
+
+	if (im < lo) {
+		if (*rstatus == 0)
+			*rstatus = ERANGE;
+		return lo;
+	}
+	if (im > hi) {
+		if (*rstatus == 0)
+			*rstatus = ERANGE;
+		return hi;
+	}
+
+	return im;
+}
diff --git a/src/strtou.c b/src/strtou.c
new file mode 100644
index 0000000..0e22a88
--- /dev/null
+++ b/src/strtou.c
@@ -0,0 +1,97 @@
+/*	$NetBSD: _strtoi.h,v 1.2 2015/01/18 17:55:22 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Original version ID:
+ * NetBSD: src/lib/libc/locale/_wcstoul.h,v 1.2 2003/08/07 16:43:03 agc Exp
+ *
+ * Created by Kamil Rytarowski, based on ID:
+ * NetBSD: src/common/lib/libc/stdlib/_strtoul.h,v 1.7 2013/05/17 12:55:56 joe…
+ */
+
+#include <sys/cdefs.h>
+
+#include <stddef.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#define _DIAGASSERT(t)
+
+uintmax_t
+strtou(const char *__restrict nptr,
+       char **__restrict endptr, int base,
+       uintmax_t lo, uintmax_t hi, int *rstatus)
+{
+	int serrno;
+	uintmax_t im;
+	char *ep;
+	int rep;
+
+	_DIAGASSERT(hi >= lo);
+
+	_DIAGASSERT(nptr != NULL);
+	/* endptr may be NULL */
+
+	if (endptr == NULL)
+		endptr = &ep;
+
+	if (rstatus == NULL)
+		rstatus = &rep;
+
+	serrno = errno;
+	errno = 0;
+
+	im = strtoumax(nptr, endptr, base);
+
+	*rstatus = errno;
+	errno = serrno;
+
+	if (*rstatus == 0) {
+		/* No digits were found */
+		if (nptr == *endptr)
+			*rstatus = ECANCELED;
+		/* There are further characters after number */
+		else if (**endptr != '\0')
+			*rstatus = ENOTSUP;
+	}
+
+	if (im < lo) {
+		if (*rstatus == 0)
+			*rstatus = ERANGE;
+		return lo;
+	}
+	if (im > hi) {
+		if (*rstatus == 0)
+			*rstatus = ERANGE;
+		return hi;
+	}
+
+	return im;
+}
commit ef5faeb575c21257347643fb157c6b64f62b6ffe
Author: Guillem Jover <guillem at hadrons.org>
Date:   Sun May 20 19:20:33 2018 +0200

    Update few RCS keyword contents to match BSD originals
    
    This will slightly reduce the delta, and makes it easier to compare the
    sources.

diff --git a/src/chacha_private.h b/src/chacha_private.h
index 5aae8d8..3b4ec93 100644
--- a/src/chacha_private.h
+++ b/src/chacha_private.h
@@ -4,7 +4,7 @@ D. J. Bernstein
 Public domain.
 */
 
-/* $OpenBSD$ */
+/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */
 
 typedef unsigned char u8;
 typedef unsigned int u32;
diff --git a/src/radixsort.c b/src/radixsort.c
index 44d3a58..e03b18d 100644
--- a/src/radixsort.c
+++ b/src/radixsort.c
@@ -1,4 +1,5 @@
-/*     $NetBSD: radixsort.c,v 1.18 2009/08/21 20:49:50 dsl Exp $       */
+/*	$NetBSD: radixsort.c,v 1.18 2009/08/21 20:49:50 dsl Exp $	*/
+
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
commit 2d7de186e9cb19a756c0630ee85cb3f2d29b3484
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 03:09:05 2018 +0200

    Update vis/unvis modules from NetBSD

diff --git a/COPYING b/COPYING
index c38a56b..4377959 100644
--- a/COPYING
+++ b/COPYING
@@ -122,7 +122,6 @@ Files:
  src/strmode.c
  src/strnstr.c
  src/unvis.c
- src/vis.c
 Copyright:
  Copyright © 1980, 1982, 1986, 1989-1994
      The Regents of the University of California.  All rights reserved.
@@ -316,6 +315,16 @@ License: BSD-2-clause-NetBSD
  POSSIBILITY OF SUCH DAMAGE.
 
 Files:
+ src/vis.c
+Copyright:
+ Copyright © 1989, 1993
+     The Regents of the University of California.  All rights reserved.
+ .
+ Copyright © 1999, 2005 The NetBSD Foundation, Inc.
+ All rights reserved.
+License: BSD-3-clause-Regents and BSD-2-clause-NetBSD
+
+Files:
  include/bsd/sys/endian.h
  man/byteorder.3bsd
  man/closefrom.3bsd
diff --git a/include/bsd/vis.h b/include/bsd/vis.h
index ab5430c..d8b6635 100644
--- a/include/bsd/vis.h
+++ b/include/bsd/vis.h
@@ -1,3 +1,5 @@
+/*	$NetBSD: vis.h,v 1.25 2017/04/23 01:57:36 christos Exp $	*/
+
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -27,7 +29,6 @@
  * SUCH DAMAGE.
  *
  *	@(#)vis.h	8.1 (Berkeley) 6/2/93
- * $FreeBSD: src/include/vis.h,v 1.11 2003/10/30 10:40:49 phk Exp $
  */
 
 #ifndef LIBBSD_VIS_H
@@ -38,25 +39,34 @@
 /*
  * to select alternate encoding format
  */
-#define	VIS_OCTAL	0x01	/* use octal \ddd format */
-#define	VIS_CSTYLE	0x02	/* use \[nrft0..] where appropriate */
+#define	VIS_OCTAL	0x0001	/* use octal \ddd format */
+#define	VIS_CSTYLE	0x0002	/* use \[nrft0..] where appropriate */
 
 /*
  * to alter set of characters encoded (default is to encode all
  * non-graphic except space, tab, and newline).
  */
-#define	VIS_SP		0x04	/* also encode space */
-#define	VIS_TAB		0x08	/* also encode tab */
-#define	VIS_NL		0x10	/* also encode newline */
+#define	VIS_SP		0x0004	/* also encode space */
+#define	VIS_TAB		0x0008	/* also encode tab */
+#define	VIS_NL		0x0010	/* also encode newline */
 #define	VIS_WHITE	(VIS_SP | VIS_TAB | VIS_NL)
-#define	VIS_SAFE	0x20	/* only encode "unsafe" characters */
+#define	VIS_SAFE	0x0020	/* only encode "unsafe" characters */
+#define	VIS_DQ		0x8000	/* also encode double quotes */
 
 /*
  * other
  */
-#define	VIS_NOSLASH	0x40	/* inhibit printing '\' */
-#define	VIS_HTTPSTYLE	0x80	/* http-style escape % HEX HEX */
-#define	VIS_GLOB	0x100	/* encode glob(3) magics */
+#define	VIS_NOSLASH	0x0040	/* inhibit printing '\' */
+#define	VIS_HTTP1808	0x0080	/* http-style escape % hex hex */
+#define	VIS_HTTPSTYLE	0x0080	/* http-style escape % hex hex */
+#define	VIS_MIMESTYLE	0x0100	/* mime-style escape = HEX HEX */
+#define	VIS_HTTP1866	0x0200	/* http-style &#num; or &string; */
+#define	VIS_NOESCAPE	0x0400	/* don't decode `\' */
+#define	_VIS_END	0x0800	/* for unvis */
+#define	VIS_GLOB	0x1000	/* encode glob(3) magic characters */
+#define	VIS_SHELL	0x2000	/* encode shell special characters [not glob] */
+#define	VIS_META	(VIS_WHITE | VIS_GLOB | VIS_SHELL)
+#define	VIS_NOLOCALE	0x4000	/* encode using the C locale */
 
 /*
  * unvis return codes
@@ -70,7 +80,7 @@
 /*
  * unvis flags
  */
-#define	UNVIS_END	1	/* no more characters */
+#define	UNVIS_END	_VIS_END	/* no more characters */
 
 #ifdef LIBBSD_OVERLAY
 #include <sys/cdefs.h>
@@ -80,12 +90,33 @@
 
 __BEGIN_DECLS
 char	*vis(char *, int, int, int);
+char	*nvis(char *, size_t, int, int, int);
+
+char	*svis(char *, int, int, int, const char *);
+char	*snvis(char *, size_t, int, int, int, const char *);
+
 int	strvis(char *, const char *, int);
+int	stravis(char **, const char *, int);
+int	strnvis(char *, size_t, const char *, int);
+
+int	strsvis(char *, const char *, int, const char *);
+int	strsnvis(char *, size_t, const char *, int, const char *);
+
 int	strvisx(char *, const char *, size_t, int);
-int	strnvis(char *, const char *, size_t, int);
+int	strnvisx(char *, size_t, const char *, size_t, int);
+int	strenvisx(char *, size_t, const char *, size_t, int, int *);
+
+int	strsvisx(char *, const char *, size_t, int, const char *);
+int	strsnvisx(char *, size_t, const char *, size_t, int, const char *);
+int	strsenvisx(char *, size_t, const char *, size_t , int, const char *,
+    int *);
+
 int	strunvis(char *, const char *);
+int	strnunvis(char *, size_t, const char *);
+
 int	strunvisx(char *, const char *, int);
-ssize_t strnunvis(char *, const char *, size_t);
+int	strnunvisx(char *, size_t, const char *, int);
+
 int	unvis(char *, int, int *, int);
 __END_DECLS
 
diff --git a/man/unvis.3bsd b/man/unvis.3bsd
index 525eae8..693fbda 100644
--- a/man/unvis.3bsd
+++ b/man/unvis.3bsd
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: unvis.3,v 1.15 2005/07/22 03:16:58 jaredy Exp $
+.\"	$NetBSD: unvis.3,v 1.29 2017/10/24 19:14:55 abhinav Exp $
 .\"
 .\" Copyright (c) 1989, 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -27,13 +27,17 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: May 31 2007 $
+.\"     @(#)unvis.3	8.2 (Berkeley) 12/11/93
+.\"
+.Dd March 12, 2011
 .Dt UNVIS 3bsd
 .Os
 .Sh NAME
 .Nm unvis ,
 .Nm strunvis ,
-.Nm strnunvis
+.Nm strnunvis ,
+.Nm strunvisx ,
+.Nm strnunvisx
 .Nd decode a visual representation of characters
 .Sh LIBRARY
 .ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
@@ -44,88 +48,91 @@
 .Xr libbsd 7
 for include usage.)
 .Ft int
-.Fn unvis "char *cp" "char c" "int *astate" "int flag"
+.Fn unvis "char *cp" "int c" "int *astate" "int flag"
+.Ft int
+.Fn strunvis "char *dst" "const char *src"
+.Ft int
+.Fn strnunvis "char *dst" "size_t dlen" "const char *src"
 .Ft int
-.Fn strunvis "char *dst" "char *src"
-.Ft ssize_t
-.Fn strnunvis "char *dst" "char *src" "size_t size"
+.Fn strunvisx "char *dst" "const char *src" "int flag"
+.Ft int
+.Fn strnunvisx "char *dst" "size_t dlen" "const char *src" "int flag"
 .Sh DESCRIPTION
 The
 .Fn unvis ,
 .Fn strunvis
 and
-.Fn strnunvis
-functions are used to decode a visual representation of characters,
-as produced by the
+.Fn strunvisx
+functions
+are used to decode a visual representation of characters, as produced
+by the
 .Xr vis 3bsd
-function, back into the original form.
+function, back into
+the original form.
+.Pp
+The
 .Fn unvis
-is called with successive characters in
-.Fa c
-until a valid
-sequence is recognized, at which time the decoded character is
-available at the character pointed to by
-.Fa cp .
+function is called with successive characters in
+.Ar c
+until a valid sequence is recognized, at which time the decoded
+character is available at the character pointed to by
+.Ar cp .
 .Pp
+The
 .Fn strunvis
-decodes the characters pointed to by
-.Fa src
+function decodes the characters pointed to by
+.Ar src
 into the buffer pointed to by
-.Fa dst .
-.Pp
-.Fn strnunvis
-decodes the characters pointed to by
-.Fa src
-into the buffer pointed to by
-.Fa dst ,
-writing a maximum of
-.Fa size
-bytes.
+.Ar dst .
 The
 .Fn strunvis
 function simply copies
-.Fa src
+.Ar src
 to
-.Fa dst ,
+.Ar dst ,
 decoding any escape sequences along the way,
 and returns the number of characters placed into
-.Fa dst ,
+.Ar dst ,
 or \-1 if an
 invalid escape sequence was detected.
 The size of
-.Fa dst
-should be
-equal to the size of
-.Fa src
+.Ar dst
+should be equal to the size of
+.Ar src
 (that is, no expansion takes place during decoding).
+.Pp
+The
+.Fn strunvisx
+function does the same as the
 .Fn strunvis
-terminates the destination string with a trailing NUL byte;
-.Fn strnunvis
-does so if
-.Fa size
-is larger than 0.
+function,
+but it allows you to add a flag that specifies the style the string
+.Ar src
+is encoded with.
+Currently, the supported flags are:
+.Dv VIS_HTTPSTYLE
+and
+.Dv VIS_MIMESTYLE .
 .Pp
 The
 .Fn unvis
-function implements a state machine that can be used to decode an arbitrary
-stream of bytes.
+function implements a state machine that can be used to decode an
+arbitrary stream of bytes.
 All state associated with the bytes being decoded is stored outside the
 .Fn unvis
 function (that is, a pointer to the state is passed in), so
 calls decoding different streams can be freely intermixed.
-To start decoding a stream of bytes, first initialize an integer
-to zero.
+To start decoding a stream of bytes, first initialize an integer to zero.
 Call
 .Fn unvis
 with each successive byte, along with a pointer
 to this integer, and a pointer to a destination character.
-.Sh RETURN VALUES
 The
 .Fn unvis
 function has several return codes that must be handled properly.
 They are:
 .Bl -tag -width UNVIS_VALIDPUSH
-.It Li \&0 (zero)
+.It Li \&0 No (zero)
 Another character is necessary; nothing has been recognized yet.
 .It Dv UNVIS_VALID
 A valid character has been recognized and is available at the location
@@ -140,30 +147,41 @@ however, the character currently passed in should be passed in again.
 A valid sequence was detected, but no character was produced.
 This return code is necessary to indicate a logical break between characters.
 .It Dv UNVIS_SYNBAD
-An invalid escape sequence was detected, or the decoder is in an
-unknown state.
+An invalid escape sequence was detected, or the decoder is in an unknown state.
 The decoder is placed into the starting state.
 .El
 .Pp
 When all bytes in the stream have been processed, call
 .Fn unvis
-one more time with
-.Fa flag
-set to
+one more time with flag set to
 .Dv UNVIS_END
 to extract any remaining character (the character passed in is ignored).
 .Pp
 The
-.Fn strunvis
-function returns the number of bytes written (not counting
-the trailing NUL byte) or \-1 if an error occurred.
+.Fa flag
+argument is also used to specify the encoding style of the source.
+If set to
+.Dv VIS_HTTPSTYLE
+or
+.Dv VIS_HTTP1808 ,
+.Fn unvis
+will decode URI strings as specified in RFC 1808.
+If set to
+.Dv VIS_HTTP1866 ,
+.Fn unvis
+will decode entity references and numeric character references
+as specified in RFC 1866.
+If set to
+.Dv VIS_MIMESTYLE ,
+.Fn unvis
+will decode MIME Quoted-Printable strings as specified in RFC 2045.
+If set to
+.Dv VIS_NOESCAPE ,
+.Fn unvis
+will not decode
+.Ql \e
+quoted characters.
 .Pp
-The
-.Fn strnunvis
-function returns the number of bytes (not counting the trailing NUL byte)
-that would be needed to fully convert the input string, or \-1 if an
-error occurred.
-.Sh EXAMPLES
 The following code fragment illustrates a proper use of
 .Fn unvis .
 .Bd -literal -offset indent
@@ -177,25 +195,72 @@ again:
 	case UNVIS_NOCHAR:
 		break;
 	case UNVIS_VALID:
-		(void) putchar(out);
+		(void)putchar(out);
 		break;
 	case UNVIS_VALIDPUSH:
-		(void) putchar(out);
+		(void)putchar(out);
 		goto again;
 	case UNVIS_SYNBAD:
-		(void)fprintf(stderr, "bad sequence!\en");
-		exit(1);
+		errx(EXIT_FAILURE, "Bad character sequence!");
 	}
 }
-if (unvis(&out, (char)0, &state, UNVIS_END) == UNVIS_VALID)
-	(void) putchar(out);
+if (unvis(&out, '\e0', &state, UNVIS_END) == UNVIS_VALID)
+	(void)putchar(out);
 .Ed
+.Sh ERRORS
+The functions
+.Fn strunvis ,
+.Fn strnunvis ,
+.Fn strunvisx ,
+and
+.Fn strnunvisx
+will return \-1 on error and set
+.Va errno
+to:
+.Bl -tag -width Er
+.It Bq Er EINVAL
+An invalid escape sequence was detected, or the decoder is in an unknown state.
+.El
+.Pp
+In addition the functions
+.Fn strnunvis
+and
+.Fn strnunvisx
+will can also set
+.Va errno
+on error to:
+.Bl -tag -width Er
+.It Bq Er ENOSPC
+Not enough space to perform the conversion.
+.El
 .Sh SEE ALSO
 .Xr unvis 1 ,
 .Xr vis 1 ,
 .Xr vis 3bsd
+.Rs
+.%A R. Fielding
+.%T Relative Uniform Resource Locators
+.%O RFC1808
+.Re
 .Sh HISTORY
 The
 .Fn unvis
-function first appeared in
+function
+first appeared in
 .Bx 4.4 .
+The
+.Fn strnunvis
+and
+.Fn strnunvisx
+functions appeared in
+.Nx 6.0 .
+.Sh BUGS
+The names
+.Dv VIS_HTTP1808
+and
+.Dv VIS_HTTP1866
+are wrong.
+Percent-encoding was defined in RFC 1738, the original RFC for URL.
+RFC 1866 defines HTML 2.0, an application of SGML, from which it
+inherits concepts of numeric character references and entity
+references.
diff --git a/man/vis.3bsd b/man/vis.3bsd
index 2215abe..25280a8 100644
--- a/man/vis.3bsd
+++ b/man/vis.3bsd
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: vis.3,v 1.23 2005/08/28 19:51:27 millert Exp $
+.\"	$NetBSD: vis.3,v 1.49 2017/08/05 20:22:29 wiz Exp $
 .\"
 .\" Copyright (c) 1989, 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -27,53 +27,87 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: May 31 2007 $
+.\"     @(#)vis.3	8.1 (Berkeley) 6/9/93
+.\"
+.Dd April 22, 2017
 .Dt VIS 3bsd
 .Os
 .Sh NAME
 .Nm vis ,
+.Nm nvis ,
 .Nm strvis ,
+.Nm stravis ,
 .Nm strnvis ,
-.Nm strvisx
+.Nm strvisx ,
+.Nm strnvisx ,
+.Nm strenvisx ,
+.Nm svis ,
+.Nm snvis ,
+.Nm strsvis ,
+.Nm strsnvis ,
+.Nm strsvisx ,
+.Nm strsnvisx ,
+.Nm strsenvisx
 .Nd visually encode characters
 .Sh LIBRARY
 .ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
 .Lb libbsd
 .Sh SYNOPSIS
-.In stdlib.h
 .In vis.h
 (See
 .Xr libbsd 7
 for include usage.)
 .Ft char *
 .Fn vis "char *dst" "int c" "int flag" "int nextc"
+.Ft char *
+.Fn nvis "char *dst" "size_t dlen" "int c" "int flag" "int nextc"
 .Ft int
 .Fn strvis "char *dst" "const char *src" "int flag"
 .Ft int
-.Fn strnvis "char *dst" "const char *src" "size_t size" "int flag"
+.Fn stravis "char **dst" "const char *src" "int flag"
+.Ft int
+.Fn strnvis "char *dst" "size_t dlen" "const char *src" "int flag"
 .Ft int
 .Fn strvisx "char *dst" "const char *src" "size_t len" "int flag"
+.Ft int
+.Fn strnvisx "char *dst" "size_t dlen" "const char *src" "size_t len" "int flag"
+.Ft int
+.Fn strenvisx "char *dst" "size_t dlen" "const char *src" "size_t len" "int flag" "int *cerr_ptr"
+.Ft char *
+.Fn svis "char *dst" "int c" "int flag" "int nextc" "const char *extra"
+.Ft char *
+.Fn snvis "char *dst" "size_t dlen" "int c" "int flag" "int nextc" "const char *extra"
+.Ft int
+.Fn strsvis "char *dst" "const char *src" "int flag" "const char *extra"
+.Ft int
+.Fn strsnvis "char *dst" "size_t dlen" "const char *src" "int flag" "const char *extra"
+.Ft int
+.Fn strsvisx "char *dst" "const char *src" "size_t len" "int flag" "const char *extra"
+.Ft int
+.Fn strsnvisx "char *dst" "size_t dlen" "const char *src" "size_t len" "int flag" "const char *extra"
+.Ft int
+.Fn strsenvisx "char *dst" "size_t dlen" "const char *src" "size_t len" "int flag" "const char *extra" "int *cerr_ptr"
 .Sh DESCRIPTION
 The
 .Fn vis
-function copies into
+function
+copies into
 .Fa dst
 a string which represents the character
 .Fa c .
 If
 .Fa c
 needs no encoding, it is copied in unaltered.
-The string is NUL terminated and a pointer to the end of the string is
+The string is null terminated, and a pointer to the end of the string is
 returned.
 The maximum length of any encoding is four
-characters (not including the trailing NUL);
+bytes (not including the trailing
+.Dv NUL ) ;
 thus, when
 encoding a set of characters into a buffer, the size of the buffer should
-be four times the number of characters encoded, plus one for the trailing
-NUL.
-The
-.Fa flag
-parameter is used for altering the default range of
+be four times the number of bytes encoded, plus one for the trailing
+.Dv NUL .
+The flag parameter is used for altering the default range of
 characters considered for encoding and for altering the visual
 representation.
 The additional character,
@@ -84,9 +118,11 @@ encoding format (explained below).
 .Pp
 The
 .Fn strvis ,
-.Fn strnvis
+.Fn stravis ,
+.Fn strnvis ,
+.Fn strvisx ,
 and
-.Fn strvisx
+.Fn strnvisx
 functions copy into
 .Fa dst
 a visual representation of
@@ -94,89 +130,153 @@ the string
 .Fa src .
 The
 .Fn strvis
-function encodes characters from
-.Fa src
-up to the first NUL.
-The
+and
 .Fn strnvis
-function encodes characters from
+functions encode characters from
 .Fa src
-up to the first NUL or the end of
-.Fa dst ,
-as indicated by
-.Fa size .
+up to the
+first
+.Dv NUL .
 The
 .Fn strvisx
-function encodes exactly
+and
+.Fn strnvisx
+functions encode exactly
 .Fa len
 characters from
 .Fa src
 (this
-is useful for encoding a block of data that may contain NULs).
-All three forms NUL terminate
-.Fa dst ,
-except for
-.Fn strnvis
-when
-.Fa size
-is zero, in which case
-.Fa dst
-is not touched.
-For
-.Fn strvis
-and
-.Fn strvisx ,
-the size of
+is useful for encoding a block of data that may contain
+.Dv NUL Ns 's ) .
+Both forms
+.Dv NUL
+terminate
+.Fa dst .
+The size of
 .Fa dst
 must be four times the number
-of characters encoded from
+of bytes encoded from
 .Fa src
-(plus one for the NUL).
-.Fn strvis
-and
-.Fn strvisx
-return the number of characters in
+(plus one for the
+.Dv NUL ) .
+Both
+forms return the number of characters in
 .Fa dst
-(not including the trailing NUL).
-.Fn strnvis
-returns the length that
+(not including the trailing
+.Dv NUL ) .
+The
+.Fn stravis
+function allocates space dynamically to hold the string.
+The
+.Dq Nm n
+versions of the functions also take an additional argument
+.Fa dlen
+that indicates the length of the
 .Fa dst
-would become if it were of unlimited size (similar to
-.Xr snprintf 3
-or
-.Xr strlcpy 3bsd ) .
-This can be used to detect truncation but it also means that
-the return value of
+buffer.
+If
+.Fa dlen
+is not large enough to fit the converted string then the
 .Fn strnvis
-must not be used without checking it against
-.Fa size .
+and
+.Fn strnvisx
+functions return \-1 and set
+.Va errno
+to
+.Dv ENOSPC .
+The
+.Fn strenvisx
+function takes an additional argument,
+.Fa cerr_ptr ,
+that is used to pass in and out a multibyte conversion error flag.
+This is useful when processing single characters at a time when
+it is possible that the locale may be set to something other
+than the locale of the characters in the input data.
+.Pp
+The functions
+.Fn svis ,
+.Fn snvis ,
+.Fn strsvis ,
+.Fn strsnvis ,
+.Fn strsvisx ,
+.Fn strsnvisx ,
+and
+.Fn strsenvisx
+correspond to
+.Fn vis ,
+.Fn nvis ,
+.Fn strvis ,
+.Fn strnvis ,
+.Fn strvisx ,
+.Fn strnvisx ,
+and
+.Fn strenvisx
+but have an additional argument
+.Fa extra ,
+pointing to a
+.Dv NUL
+terminated list of characters.
+These characters will be copied encoded or backslash-escaped into
+.Fa dst .
+These functions are useful e.g. to remove the special meaning
+of certain characters to shells.
 .Pp
 The encoding is a unique, invertible representation composed entirely of
 graphic characters; it can be decoded back into the original form using
 the
-.Xr unvis 3bsd
-or
+.Xr unvis 3bsd ,
 .Xr strunvis 3bsd
+or
+.Xr strnunvis 3bsd
 functions.
 .Pp
 There are two parameters that can be controlled: the range of
-characters that are encoded, and the type
-of representation used.
-By default, all non-graphic characters
-except space, tab, and newline are encoded
-(see
+characters that are encoded (applies only to
+.Fn vis ,
+.Fn nvis ,
+.Fn strvis ,
+.Fn strnvis ,
+.Fn strvisx ,
+and
+.Fn strnvisx ) ,
+and the type of representation used.
+By default, all non-graphic characters,
+except space, tab, and newline are encoded (see
 .Xr isgraph 3 ) .
 The following flags
 alter this:
 .Bl -tag -width VIS_WHITEX
+.It Dv VIS_DQ
+Also encode double quotes
 .It Dv VIS_GLOB
-Also encode magic characters recognized by
-.Xr glob 3
-.Pf ( Ql * ,
+Also encode the magic characters
+.Ql ( * ,
 .Ql \&? ,
-.Ql \&[ )
+.Ql \&[ ,
 and
-.Ql # .
+.Ql # )
+recognized by
+.Xr glob 3 .
+.It Dv VIS_SHELL
+Also encode the meta characters used by shells (in addition to the glob
+characters):
+.Ql ( ' ,
+.Ql ` ,
+.Ql \&" ,
+.Ql \&; ,
+.Ql & ,
+.Ql < ,
+.Ql > ,
+.Ql \&( ,
+.Ql \&) ,
+.Ql \&| ,
+.Ql \&] ,
+.Ql \e ,
+.Ql $ ,
+.Ql \&! ,
+.Ql \&^ ,
+and
+.Ql ~ ) .
 .It Dv VIS_SP
 Also encode space.
 .It Dv VIS_TAB
@@ -185,34 +285,56 @@ Also encode tab.
 Also encode newline.
 .It Dv VIS_WHITE
 Synonym for
-.Dv VIS_SP
-\&|
-.Dv VIS_TAB
-\&|
-.Dv VIS_NL .
+.Dv VIS_SP | VIS_TAB | VIS_NL .
+.It Dv VIS_META
+Synonym for
+.Dv VIS_WHITE | VIS_GLOB | VIS_SHELL .
 .It Dv VIS_SAFE
 Only encode
 .Dq unsafe
 characters.
-These are control characters which may cause common terminals to perform
+Unsafe means control characters which may cause common terminals to perform
 unexpected functions.
-Currently this form allows space,
-tab, newline, backspace, bell, and return -- in addition
-to all graphic characters -- unencoded.
+Currently this form allows space, tab, newline, backspace, bell, and
+return \(em in addition to all graphic characters \(em unencoded.
 .El
 .Pp
-There are three forms of encoding.
-All forms use the backslash
+(The above flags have no effect for
+.Fn svis ,
+.Fn snvis ,
+.Fn strsvis ,
+.Fn strsnvis ,
+.Fn strsvisx ,
+and
+.Fn strsnvisx .
+When using these functions, place all graphic characters to be
+encoded in an array pointed to by
+.Fa extra .
+In general, the backslash character should be included in this array, see the
+warning on the use of the
+.Dv VIS_NOSLASH
+flag below).
+.Pp
+There are six forms of encoding.
+All forms use the backslash character
 .Ql \e
-character to introduce a special
-sequence; two backslashes are used to represent a real backslash.
+to introduce a special
+sequence; two backslashes are used to represent a real backslash,
+except
+.Dv VIS_HTTPSTYLE
+that uses
+.Ql % ,
+or
+.Dv VIS_MIMESTYLE
+that uses
+.Ql = .
 These are the visual formats:
 .Bl -tag -width VIS_CSTYLE
 .It (default)
 Use an
 .Ql M
 to represent meta characters (characters with the 8th
-bit set), and use a caret
+bit set), and use caret
 .Ql ^
 to represent control characters (see
 .Xr iscntrl 3 ) .
@@ -256,27 +378,27 @@ space.
 .It Dv \e240
 Represents Meta-space.
 .El
-.Pp
 .It Dv VIS_CSTYLE
 Use C-style backslash sequences to represent standard non-printable
 characters.
 The following sequences are used to represent the indicated characters:
 .Bd -unfilled -offset indent
-.Li \ea Tn  - BEL No (007)
-.Li \eb Tn  - BS No (010)
-.Li \ef Tn  - NP No (014)
-.Li \en Tn  - NL No (012)
-.Li \er Tn  - CR No (015)
-.Li \es Tn  - SP No (040)
-.Li \et Tn  - HT No (011)
-.Li \ev Tn  - VT No (013)
-.Li \e0 Tn  - NUL No (000)
+.Li \ea Tn  \(em BEL No (007)
+.Li \eb Tn  \(em BS No (010)
+.Li \ef Tn  \(em NP No (014)
+.Li \en Tn  \(em NL No (012)
+.Li \er Tn  \(em CR No (015)
+.Li \es Tn  \(em SP No (040)
+.Li \et Tn  \(em HT No (011)
+.Li \ev Tn  \(em VT No (013)
+.Li \e0 Tn  \(em NUL No (000)
 .Ed
 .Pp
 When using this format, the
 .Fa nextc
-parameter is looked at to determine
-if a NUL character can be encoded as
+parameter is looked at to determine if a
+.Dv NUL
+character can be encoded as
 .Ql \e0
 instead of
 .Ql \e000 .
@@ -284,13 +406,36 @@ If
 .Fa nextc
 is an octal digit, the latter representation is used to
 avoid ambiguity.
+.Pp
+Non-printable characters without C-style
+backslash sequences use the default representation.
 .It Dv VIS_OCTAL
 Use a three digit octal sequence.
 The form is
 .Ql \eddd
 where
-.Ar d
+.Em d
 represents an octal digit.
+.It Dv VIS_CSTYLE \&| Dv VIS_OCTAL
+Same as
+.Dv VIS_CSTYLE
+except that non-printable characters without C-style
+backslash sequences use a three digit octal sequence.
+.It Dv VIS_HTTPSTYLE
+Use URI encoding as described in RFC 1738.
+The form is
+.Ql %xx
+where
+.Em x
+represents a lower case hexadecimal digit.
+.It Dv VIS_MIMESTYLE
+Use MIME Quoted-Printable encoding as described in RFC 2045, only don't
+break lines and don't handle CRLF.
+The form is
+.Ql =XX
+where
+.Em X
+represents an upper case hexadecimal digit.
 .El
 .Pp
 There is one additional flag,
@@ -304,21 +449,112 @@ meta characters as
 .Ql M-C ) .
 With this flag set, the encoding is
 ambiguous and non-invertible.
+.Sh MULTIBYTE CHARACTER SUPPORT
+These functions support multibyte character input.
+The encoding conversion is influenced by the setting of the
+.Ev LC_CTYPE
+environment variable which defines the set of characters
+that can be copied without encoding.
+.Pp
+If
+.Dv VIS_NOLOCALE
+is set, processing is done assuming the C locale and overriding
+any other environment settings.
+.Pp
+When 8-bit data is present in the input,
+.Ev LC_CTYPE
+must be set to the correct locale or to the C locale.
+If the locales of the data and the conversion are mismatched,
+multibyte character recognition may fail and encoding will be performed
+byte-by-byte instead.
+.Pp
+As noted above,
+.Fa dst
+must be four times the number of bytes processed from
+.Fa src .
+But note that each multibyte character can be up to
+.Dv MB_LEN_MAX
+bytes
+.\" (see
+.\" .Xr multibyte 3 )
+so in terms of multibyte characters,
+.Fa dst
+must be four times
+.Dv MB_LEN_MAX
+times the number of characters processed from
+.Fa src .
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev LC_CTYPE"
+.It Ev LC_CTYPE
+Specify the locale of the input data.
+Set to C if the input data locale is unknown.
+.El
+.Sh ERRORS
+The functions
+.Fn nvis
+and
+.Fn snvis
+will return
+.Dv NULL
+and the functions
+.Fn strnvis ,
+.Fn strnvisx ,
+.Fn strsnvis ,
+and
+.Fn strsnvisx ,
+will return \-1 when the
+.Fa dlen
+destination buffer size is not enough to perform the conversion while
+setting
+.Va errno
+to:
+.Bl -tag -width ".Bq Er ENOSPC"
+.It Bq Er ENOSPC
+The destination buffer size is not large enough to perform the conversion.
+.El
 .Sh SEE ALSO
 .Xr unvis 1 ,
 .Xr vis 1 ,
-.Xr snprintf 3 ,
-.Xr strlcpy 3bsd ,
+.Xr glob 3 ,
+.\" .Xr multibyte 3 ,
 .Xr unvis 3bsd
+.Rs
+.%A T. Berners-Lee
+.%T Uniform Resource Locators (URL)
+.%O "RFC 1738"
+.Re
+.Rs
+.%T "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies"
+.%O "RFC 2045"
+.Re
 .Sh HISTORY
 The
 .Fn vis ,
-.Fn strvis
+.Fn strvis ,
 and
 .Fn strvisx
 functions first appeared in
 .Bx 4.4 .
 The
-.Fn strnvis
-function first appeared in
-.Ox 2.9 .
+.Fn svis ,
+.Fn strsvis ,
+and
+.Fn strsvisx
+functions appeared in
+.Nx 1.5 .
+The buffer size limited versions of the functions
+.Po Fn nvis ,
+.Fn strnvis ,
+.Fn strnvisx ,
+.Fn snvis ,
+.Fn strsnvis ,
+and
+.Fn strsnvisx Pc
+appeared in
+.Nx 6.0
+and
+.Fx 9.2 .
+Multibyte character support was added in
+.Nx 7.0
+and
+.Fx 9.2 .
diff --git a/src/libbsd.map b/src/libbsd.map
index 4a41586..a91d522 100644
--- a/src/libbsd.map
+++ b/src/libbsd.map
@@ -144,4 +144,16 @@ LIBBSD_0.9 {
     flopenat;
 
     pidfile_fileno;
+
+    nvis;
+    snvis;
+    stravis;
+    strenvisx;
+    strnunvisx;
+    strsenvisx;
+    strsnvis;
+    strsnvisx;
+    strsvis;
+    strsvisx;
+    svis;
 } LIBBSD_0.8;
diff --git a/src/unvis.c b/src/unvis.c
index c45a5f8..e16bd50 100644
--- a/src/unvis.c
+++ b/src/unvis.c
@@ -1,3 +1,5 @@
+/*	$NetBSD: unvis.c,v 1.44 2014/09/26 15:43:36 roy Exp $	*/
+
 /*-
  * Copyright (c) 1989, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -27,10 +29,22 @@
  * SUCH DAMAGE.
  */
 
+#include <sys/cdefs.h>
 #include <sys/types.h>
+
+#include <assert.h>
 #include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
 #include <vis.h>
 
+#ifdef __weak_alias
+__weak_alias(strnunvisx,_strnunvisx)
+#endif
+
+#define _DIAGASSERT(x)
+
 /*
  * decode driven by state machine
  */
@@ -41,15 +55,128 @@
 #define	S_CTRL		4	/* control char started (^) */
 #define	S_OCTAL2	5	/* octal digit 2 */
 #define	S_OCTAL3	6	/* octal digit 3 */
-#define	S_HEX2		7	/* hex digit 2 */
+#define	S_HEX		7	/* mandatory hex digit */
+#define	S_HEX1		8	/* http hex digit */
+#define	S_HEX2		9	/* http hex digit 2 */
+#define	S_MIME1		10	/* mime hex digit 1 */
+#define	S_MIME2		11	/* mime hex digit 2 */
+#define	S_EATCRNL	12	/* mime eating CRNL */
+#define	S_AMP		13	/* seen & */
+#define	S_NUMBER	14	/* collecting number */
+#define	S_STRING	15	/* collecting string */
 
-#define	S_HTTP		0x080	/* %HEXHEX escape */
+#define	isoctal(c)	(((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define	xtod(c)		(isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
+#define	XTOD(c)		(isdigit(c) ? (c - '0') : ((c - 'A') + 10))
 
-#define	isoctal(c) \
-	(((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7')
-#define	ishex(c) \
-	((((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '9') || \
-	 (((unsigned char)(c)) >= 'a' && ((unsigned char)(c)) <= 'f'))
+/*
+ * RFC 1866
+ */
+static const struct nv {
+	char name[7];
+	uint8_t value;
+} nv[] = {
+	{ "AElig",	198 }, /* capital AE diphthong (ligature)  */
+	{ "Aacute",	193 }, /* capital A, acute accent  */
+	{ "Acirc",	194 }, /* capital A, circumflex accent  */
+	{ "Agrave",	192 }, /* capital A, grave accent  */
+	{ "Aring",	197 }, /* capital A, ring  */
+	{ "Atilde",	195 }, /* capital A, tilde  */
+	{ "Auml",	196 }, /* capital A, dieresis or umlaut mark  */
+	{ "Ccedil",	199 }, /* capital C, cedilla  */
+	{ "ETH",	208 }, /* capital Eth, Icelandic  */
+	{ "Eacute",	201 }, /* capital E, acute accent  */
+	{ "Ecirc",	202 }, /* capital E, circumflex accent  */
+	{ "Egrave",	200 }, /* capital E, grave accent  */
+	{ "Euml",	203 }, /* capital E, dieresis or umlaut mark  */
+	{ "Iacute",	205 }, /* capital I, acute accent  */
+	{ "Icirc",	206 }, /* capital I, circumflex accent  */
+	{ "Igrave",	204 }, /* capital I, grave accent  */
+	{ "Iuml",	207 }, /* capital I, dieresis or umlaut mark  */
+	{ "Ntilde",	209 }, /* capital N, tilde  */
+	{ "Oacute",	211 }, /* capital O, acute accent  */
+	{ "Ocirc",	212 }, /* capital O, circumflex accent  */
+	{ "Ograve",	210 }, /* capital O, grave accent  */
+	{ "Oslash",	216 }, /* capital O, slash  */
+	{ "Otilde",	213 }, /* capital O, tilde  */
+	{ "Ouml",	214 }, /* capital O, dieresis or umlaut mark  */
+	{ "THORN",	222 }, /* capital THORN, Icelandic  */
+	{ "Uacute",	218 }, /* capital U, acute accent  */
+	{ "Ucirc",	219 }, /* capital U, circumflex accent  */
+	{ "Ugrave",	217 }, /* capital U, grave accent  */
+	{ "Uuml",	220 }, /* capital U, dieresis or umlaut mark  */
+	{ "Yacute",	221 }, /* capital Y, acute accent  */
+	{ "aacute",	225 }, /* small a, acute accent  */
+	{ "acirc",	226 }, /* small a, circumflex accent  */
+	{ "acute",	180 }, /* acute accent  */
+	{ "aelig",	230 }, /* small ae diphthong (ligature)  */
+	{ "agrave",	224 }, /* small a, grave accent  */
+	{ "amp",	 38 }, /* ampersand  */
+	{ "aring",	229 }, /* small a, ring  */
+	{ "atilde",	227 }, /* small a, tilde  */
+	{ "auml",	228 }, /* small a, dieresis or umlaut mark  */
+	{ "brvbar",	166 }, /* broken (vertical) bar  */
+	{ "ccedil",	231 }, /* small c, cedilla  */
+	{ "cedil",	184 }, /* cedilla  */
+	{ "cent",	162 }, /* cent sign  */
+	{ "copy",	169 }, /* copyright sign  */
+	{ "curren",	164 }, /* general currency sign  */
+	{ "deg",	176 }, /* degree sign  */
+	{ "divide",	247 }, /* divide sign  */
+	{ "eacute",	233 }, /* small e, acute accent  */
+	{ "ecirc",	234 }, /* small e, circumflex accent  */
+	{ "egrave",	232 }, /* small e, grave accent  */
+	{ "eth",	240 }, /* small eth, Icelandic  */
+	{ "euml",	235 }, /* small e, dieresis or umlaut mark  */
+	{ "frac12",	189 }, /* fraction one-half  */
+	{ "frac14",	188 }, /* fraction one-quarter  */
+	{ "frac34",	190 }, /* fraction three-quarters  */
+	{ "gt",		 62 }, /* greater than  */
+	{ "iacute",	237 }, /* small i, acute accent  */
+	{ "icirc",	238 }, /* small i, circumflex accent  */
+	{ "iexcl",	161 }, /* inverted exclamation mark  */
+	{ "igrave",	236 }, /* small i, grave accent  */
+	{ "iquest",	191 }, /* inverted question mark  */
+	{ "iuml",	239 }, /* small i, dieresis or umlaut mark  */
+	{ "laquo",	171 }, /* angle quotation mark, left  */
+	{ "lt",		 60 }, /* less than  */
+	{ "macr",	175 }, /* macron  */
+	{ "micro",	181 }, /* micro sign  */
+	{ "middot",	183 }, /* middle dot  */
+	{ "nbsp",	160 }, /* no-break space  */
+	{ "not",	172 }, /* not sign  */
+	{ "ntilde",	241 }, /* small n, tilde  */
+	{ "oacute",	243 }, /* small o, acute accent  */
+	{ "ocirc",	244 }, /* small o, circumflex accent  */
+	{ "ograve",	242 }, /* small o, grave accent  */
+	{ "ordf",	170 }, /* ordinal indicator, feminine  */
+	{ "ordm",	186 }, /* ordinal indicator, masculine  */
+	{ "oslash",	248 }, /* small o, slash  */
+	{ "otilde",	245 }, /* small o, tilde  */
+	{ "ouml",	246 }, /* small o, dieresis or umlaut mark  */
+	{ "para",	182 }, /* pilcrow (paragraph sign)  */
+	{ "plusmn",	177 }, /* plus-or-minus sign  */
+	{ "pound",	163 }, /* pound sterling sign  */
+	{ "quot",	 34 }, /* double quote  */
+	{ "raquo",	187 }, /* angle quotation mark, right  */
+	{ "reg",	174 }, /* registered sign  */
+	{ "sect",	167 }, /* section sign  */
+	{ "shy",	173 }, /* soft hyphen  */
+	{ "sup1",	185 }, /* superscript one  */
+	{ "sup2",	178 }, /* superscript two  */
+	{ "sup3",	179 }, /* superscript three  */
+	{ "szlig",	223 }, /* small sharp s, German (sz ligature)  */
+	{ "thorn",	254 }, /* small thorn, Icelandic  */
+	{ "times",	215 }, /* multiply sign  */
+	{ "uacute",	250 }, /* small u, acute accent  */
+	{ "ucirc",	251 }, /* small u, circumflex accent  */
+	{ "ugrave",	249 }, /* small u, grave accent  */
+	{ "uml",	168 }, /* umlaut (dieresis)  */
+	{ "uuml",	252 }, /* small u, dieresis or umlaut mark  */
+	{ "yacute",	253 }, /* small y, acute accent  */
+	{ "yen",	165 }, /* yen sign  */
+	{ "yuml",	255 }, /* small y, dieresis or umlaut mark  */
+};
 
 /*
  * unvis - decode characters previously encoded by vis
@@ -57,276 +184,367 @@
 int
 unvis(char *cp, int c, int *astate, int flag)
 {
+	unsigned char uc = (unsigned char)c;
+	unsigned char st, ia, is, lc;
+
+/*
+ * Bottom 8 bits of astate hold the state machine state.
+ * Top 8 bits hold the current character in the http 1866 nv string decoding
+ */
+#define GS(a)		((a) & 0xff)
+#define SS(a, b)	(((uint32_t)(a) << 24) | (b))
+#define GI(a)		((uint32_t)(a) >> 24)
+
+	_DIAGASSERT(cp != NULL);
+	_DIAGASSERT(astate != NULL);
+	st = GS(*astate);
 
 	if (flag & UNVIS_END) {
-		if (*astate == S_OCTAL2 || *astate == S_OCTAL3) {
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+		switch (st) {
+		case S_OCTAL2:
+		case S_OCTAL3:
+		case S_HEX2:
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
+		case S_GROUND:
+			return UNVIS_NOCHAR;
+		default:
+			return UNVIS_SYNBAD;
 		}
-		return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD);
 	}
 
-	switch (*astate & ~S_HTTP) {
+	switch (st) {
 
 	case S_GROUND:
 		*cp = 0;
-		if (c == '\\') {
-			*astate = S_START;
-			return (0);
+		if ((flag & VIS_NOESCAPE) == 0 && c == '\\') {
+			*astate = SS(0, S_START);
+			return UNVIS_NOCHAR;
 		}
-		if (flag & VIS_HTTPSTYLE && c == '%') {
-			*astate = S_START | S_HTTP;
-			return (0);
+		if ((flag & VIS_HTTP1808) && c == '%') {
+			*astate = SS(0, S_HEX1);
+			return UNVIS_NOCHAR;
+		}
+		if ((flag & VIS_HTTP1866) && c == '&') {
+			*astate = SS(0, S_AMP);
+			return UNVIS_NOCHAR;
+		}
+		if ((flag & VIS_MIMESTYLE) && c == '=') {
+			*astate = SS(0, S_MIME1);
+			return UNVIS_NOCHAR;
 		}
 		*cp = c;
-		return (UNVIS_VALID);
+		return UNVIS_VALID;
 
 	case S_START:
-		if (*astate & S_HTTP) {
-		    if (ishex(tolower(c))) {
-			*cp = isdigit(c) ? (c - '0') : (tolower(c) - 'a');
-			*astate = S_HEX2;
-			return (0);
-		    }
-		}
 		switch(c) {
 		case '\\':
 			*cp = c;
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
 		case '0': case '1': case '2': case '3':
 		case '4': case '5': case '6': case '7':
 			*cp = (c - '0');
-			*astate = S_OCTAL2;
-			return (0);
+			*astate = SS(0, S_OCTAL2);
+			return UNVIS_NOCHAR;
 		case 'M':
-			*cp = 0200;
-			*astate = S_META;
-			return (0);
+			*cp = (char)0200;
+			*astate = SS(0, S_META);
+			return UNVIS_NOCHAR;
 		case '^':
-			*astate = S_CTRL;
-			return (0);
+			*astate = SS(0, S_CTRL);
+			return UNVIS_NOCHAR;
 		case 'n':
 			*cp = '\n';
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
 		case 'r':
 			*cp = '\r';
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
 		case 'b':
 			*cp = '\b';
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
 		case 'a':
 			*cp = '\007';
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
 		case 'v':
 			*cp = '\v';
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
 		case 't':
 			*cp = '\t';
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
 		case 'f':
 			*cp = '\f';
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
 		case 's':
 			*cp = ' ';
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
 		case 'E':
 			*cp = '\033';
-			*astate = S_GROUND;
-			return (UNVIS_VALID);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
+		case 'x':
+			*astate = SS(0, S_HEX);
+			return UNVIS_NOCHAR;
 		case '\n':
 			/*
 			 * hidden newline
 			 */
-			*astate = S_GROUND;
-			return (UNVIS_NOCHAR);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_NOCHAR;
 		case '$':
 			/*
 			 * hidden marker
 			 */
-			*astate = S_GROUND;
-			return (UNVIS_NOCHAR);
+			*astate = SS(0, S_GROUND);
+			return UNVIS_NOCHAR;
+		default:
+			if (isgraph(c)) {
+				*cp = c;
+				*astate = SS(0, S_GROUND);
+				return UNVIS_VALID;
+			}
 		}
-		*astate = S_GROUND;
-		return (UNVIS_SYNBAD);
+		goto bad;
 
 	case S_META:
 		if (c == '-')
-			*astate = S_META1;
+			*astate = SS(0, S_META1);
 		else if (c == '^')
-			*astate = S_CTRL;
-		else {
-			*astate = S_GROUND;
-			return (UNVIS_SYNBAD);
-		}
-		return (0);
+			*astate = SS(0, S_CTRL);
+		else
+			goto bad;
+		return UNVIS_NOCHAR;
 
 	case S_META1:
-		*astate = S_GROUND;
+		*astate = SS(0, S_GROUND);
 		*cp |= c;
-		return (UNVIS_VALID);
+		return UNVIS_VALID;
 
 	case S_CTRL:
 		if (c == '?')
 			*cp |= 0177;
 		else
 			*cp |= c & 037;
-		*astate = S_GROUND;
-		return (UNVIS_VALID);
+		*astate = SS(0, S_GROUND);
+		return UNVIS_VALID;
 
 	case S_OCTAL2:	/* second possible octal digit */
-		if (isoctal(c)) {
+		if (isoctal(uc)) {
 			/*
 			 * yes - and maybe a third
 			 */
 			*cp = (*cp << 3) + (c - '0');
-			*astate = S_OCTAL3;
-			return (0);
+			*astate = SS(0, S_OCTAL3);
+			return UNVIS_NOCHAR;
 		}
 		/*
 		 * no - done with current sequence, push back passed char
 		 */
-		*astate = S_GROUND;
-		return (UNVIS_VALIDPUSH);
+		*astate = SS(0, S_GROUND);
+		return UNVIS_VALIDPUSH;
 
 	case S_OCTAL3:	/* third possible octal digit */
-		*astate = S_GROUND;
-		if (isoctal(c)) {
+		*astate = SS(0, S_GROUND);
+		if (isoctal(uc)) {
 			*cp = (*cp << 3) + (c - '0');
-			return (UNVIS_VALID);
+			return UNVIS_VALID;
 		}
 		/*
 		 * we were done, push back passed char
 		 */
-		return (UNVIS_VALIDPUSH);
+		return UNVIS_VALIDPUSH;
 
-	case S_HEX2:	/* second mandatory hex digit */
-		if (ishex(tolower(c))) {
-			*cp = (isdigit(c) ? (*cp << 4) + (c - '0') : (*cp << 4) + (tolower(c) - 'a' + 10));
+	case S_HEX:
+		if (!isxdigit(uc))
+			goto bad;
+		/*FALLTHROUGH*/
+	case S_HEX1:
+		if (isxdigit(uc)) {
+			*cp = xtod(uc);
+			*astate = SS(0, S_HEX2);
+			return UNVIS_NOCHAR;
 		}
+		/*
+		 * no - done with current sequence, push back passed char
+		 */
+		*astate = SS(0, S_GROUND);
+		return UNVIS_VALIDPUSH;
+
+	case S_HEX2:
 		*astate = S_GROUND;
-		return (UNVIS_VALID);
+		if (isxdigit(uc)) {
+			*cp = xtod(uc) | (*cp << 4);
+			return UNVIS_VALID;
+		}
+		return UNVIS_VALIDPUSH;
+
+	case S_MIME1:
+		if (uc == '\n' || uc == '\r') {
+			*astate = SS(0, S_EATCRNL);
+			return UNVIS_NOCHAR;
+		}
+		if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) {
+			*cp = XTOD(uc);
+			*astate = SS(0, S_MIME2);
+			return UNVIS_NOCHAR;
+		}
+		goto bad;
+
+	case S_MIME2:
+		if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) {
+			*astate = SS(0, S_GROUND);
+			*cp = XTOD(uc) | (*cp << 4);
+			return UNVIS_VALID;
+		}
+		goto bad;
+
+	case S_EATCRNL:
+		switch (uc) {
+		case '\r':
+		case '\n':
+			return UNVIS_NOCHAR;
+		case '=':
+			*astate = SS(0, S_MIME1);
+			return UNVIS_NOCHAR;
+		default:
+			*cp = uc;
+			*astate = SS(0, S_GROUND);
+			return UNVIS_VALID;
+		}
+
+	case S_AMP:
+		*cp = 0;
+		if (uc == '#') {
+			*astate = SS(0, S_NUMBER);
+			return UNVIS_NOCHAR;
+		}
+		*astate = SS(0, S_STRING);
+		/*FALLTHROUGH*/
+
+	case S_STRING:
+		ia = *cp;		/* index in the array */
+		is = GI(*astate);	/* index in the string */
+		lc = is == 0 ? 0 : nv[ia].name[is - 1];	/* last character */
+
+		if (uc == ';')
+			uc = '\0';
+
+		for (; ia < __arraycount(nv); ia++) {
+			if (is != 0 && nv[ia].name[is - 1] != lc)
+				goto bad;
+			if (nv[ia].name[is] == uc)
+				break;
+		}
+
+		if (ia == __arraycount(nv))
+			goto bad;
+
+		if (uc != 0) {
+			*cp = ia;
+			*astate = SS(is + 1, S_STRING);
+			return UNVIS_NOCHAR;
+		}
+
+		*cp = nv[ia].value;
+		*astate = SS(0, S_GROUND);
+		return UNVIS_VALID;
+
+	case S_NUMBER:
+		if (uc == ';')
+			return UNVIS_VALID;
+		if (!isdigit(uc))
+			goto bad;
+		*cp += (*cp * 10) + uc - '0';
+		return UNVIS_NOCHAR;
 
 	default:
+	bad:
 		/*
 		 * decoder in unknown state - (probably uninitialized)
 		 */
-		*astate = S_GROUND;
-		return (UNVIS_SYNBAD);
+		*astate = SS(0, S_GROUND);
+		return UNVIS_SYNBAD;
 	}
 }
 
 /*
- * strunvis - decode src into dst
+ * strnunvisx - decode src into dst
  *
  *	Number of chars decoded into dst is returned, -1 on error.
  *	Dst is null terminated.
  */
 
 int
-strunvis(char *dst, const char *src)
+strnunvisx(char *dst, size_t dlen, const char *src, int flag)
 {
 	char c;
-	char *start = dst;
+	char t = '\0', *start = dst;
 	int state = 0;
 
-	while ((c = *src++)) {
-	again:
-		switch (unvis(dst, c, &state, 0)) {
+	_DIAGASSERT(src != NULL);
+	_DIAGASSERT(dst != NULL);
+#define CHECKSPACE() \
+	do { \
+		if (dlen-- == 0) { \
+			errno = ENOSPC; \
+			return -1; \
+		} \
+	} while (/*CONSTCOND*/0)
+
+	while ((c = *src++) != '\0') {
+ again:
+		switch (unvis(&t, c, &state, flag)) {
 		case UNVIS_VALID:
-			dst++;
+			CHECKSPACE();
+			*dst++ = t;
 			break;
 		case UNVIS_VALIDPUSH:
-			dst++;
+			CHECKSPACE();
+			*dst++ = t;
 			goto again;
 		case 0:
 		case UNVIS_NOCHAR:
 			break;
+		case UNVIS_SYNBAD:
+			errno = EINVAL;
+			return -1;
 		default:
-			*dst = '\0';
-			return (-1);
+			_DIAGASSERT(/*CONSTCOND*/0);
+			errno = EINVAL;
+			return -1;
 		}
 	}
-	if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID)
-		dst++;
+	if (unvis(&t, c, &state, UNVIS_END) == UNVIS_VALID) {
+		CHECKSPACE();
+		*dst++ = t;
+	}
+	CHECKSPACE();
 	*dst = '\0';
-	return (dst - start);
+	return (int)(dst - start);
 }
 
-ssize_t
-strnunvis(char *dst, const char *src, size_t sz)
+int
+strunvisx(char *dst, const char *src, int flag)
 {
-	char c, p;
-	char *start = dst, *end = dst + sz - 1;
-	int state = 0;
-
-	if (sz > 0)
-		*end = '\0';
-	while ((c = *src++)) {
-	again:
-		switch (unvis(&p, c, &state, 0)) {
-		case UNVIS_VALID:
-			if (dst < end)
-				*dst = p;
-			dst++;
-			break;
-		case UNVIS_VALIDPUSH:
-			if (dst < end)
-				*dst = p;
-			dst++;
-			goto again;
-		case 0:
-		case UNVIS_NOCHAR:
-			break;
-		default:
-			if (dst <= end)
-				*dst = '\0';
-			return (-1);
-		}
-	}
-	if (unvis(&p, c, &state, UNVIS_END) == UNVIS_VALID) {
-		if (dst < end)
-			*dst = p;
-		dst++;
-	}
-	if (dst <= end)
-		*dst = '\0';
-	return (dst - start);
+	return strnunvisx(dst, (size_t)~0, src, flag);
 }
 
 int
-strunvisx(char *dst, const char *src, int flag)
+strunvis(char *dst, const char *src)
 {
-	char c;
-	char *start = dst;
-	int state = 0;
+	return strnunvisx(dst, (size_t)~0, src, 0);
+}
 
-	while ((c = *src++)) {
-	again:
-		switch (unvis(dst, c, &state, flag)) {
-		case UNVIS_VALID:
-			dst++;
-			break;
-		case UNVIS_VALIDPUSH:
-			dst++;
-			goto again;
-		case 0:
-		case UNVIS_NOCHAR:
-			break;
-		default:
-			return (-1);
-		}
-	}
-	if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID)
-		dst++;
-	*dst = '\0';
-	return (dst - start);
+int
+strnunvis(char *dst, size_t dlen, const char *src)
+{
+	return strnunvisx(dst, dlen, src, 0);
 }
diff --git a/src/vis.c b/src/vis.c
index c9a06e8..5599439 100644
--- a/src/vis.c
+++ b/src/vis.c
@@ -1,4 +1,5 @@
-/*	$OpenBSD: vis.c,v 1.18 2005/08/29 18:38:41 otto Exp $ */
+/*	$NetBSD: vis.c,v 1.74 2017/11/27 16:37:21 christos Exp $	*/
+
 /*-
  * Copyright (c) 1989, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -28,220 +29,717 @@
  * SUCH DAMAGE.
  */
 
+/*-
+ * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
 #include <sys/types.h>
-#include <limits.h>
+#include <sys/param.h>
+
+#include <assert.h>
+#include <vis.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#ifdef __weak_alias
+__weak_alias(strvisx,_strvisx)
+#endif
+
 #include <ctype.h>
-#include <string.h>
+#include <limits.h>
 #include <stdio.h>
-#include <vis.h>
+#include <string.h>
 
-#define	isoctal(c)	\
-	(((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7')
-#define	isvisible(c)							\
-	(((unsigned int)(c) <= UCHAR_MAX &&				\
-	  isascii((unsigned char)(c)) &&				\
-	(((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') ||	\
-		(flag & VIS_GLOB) == 0) &&				\
-	  isgraph((unsigned char)(c))) ||				\
-	((flag & VIS_SP) == 0 && (c) == ' ') ||				\
-	((flag & VIS_TAB) == 0 && (c) == '\t') ||			\
-	((flag & VIS_NL) == 0 && (c) == '\n') ||			\
-	((flag & VIS_SAFE) && ((c) == '\b' ||				\
-		(c) == '\007' || (c) == '\r' ||				\
-		isgraph((unsigned char)(c)))))
+#define _DIAGASSERT(x)
 
 /*
- * vis - visually encode characters
+ * The reason for going through the trouble to deal with character encodings
+ * in vis(3), is that we use this to safe encode output of commands. This
+ * safe encoding varies depending on the character set. For example if we
+ * display ps output in French, we don't want to display French characters
+ * as M-foo.
  */
-char *
-vis(char *dst, int c, int flag, int nextc)
-{
-	c = (unsigned char)c;
-
-	if (flag & VIS_HTTPSTYLE) {
-		/* Described in RFC 1808 */
-		if (!(isalnum(c) /* alpha-numeric */
-		    /* safe */
-		    || c == '$' || c == '-' || c == '_' || c == '.' || c == '+'
-		    /* extra */
-		    || c == '!' || c == '*' || c == '\'' || c == '('
-		    || c == ')' || c == ',')) {
-			*dst++ = '%';
-			snprintf(dst, 4, (c < 16 ? "0%X" : "%X"), c);
-			dst += 2;
-			goto done;
+
+static wchar_t *do_svis(wchar_t *, wint_t, int, wint_t, const wchar_t *);
+
+#undef BELL
+#define BELL L'\a'
+
+#if defined(LC_C_LOCALE)
+#define iscgraph(c)      isgraph_l(c, LC_C_LOCALE)
+#else
+/* Keep it simple for now, no locale stuff */
+#define iscgraph(c)	isgraph(c)
+#ifdef notyet
+#include <locale.h>
+static int
+iscgraph(int c) {
+	int rv;
+	char *ol;
+
+	ol = setlocale(LC_CTYPE, "C");
+	rv = isgraph(c);
+	if (ol)
+		setlocale(LC_CTYPE, ol);
+	return rv;
+}
+#endif
+#endif
+
+#define ISGRAPH(flags, c) \
+    (((flags) & VIS_NOLOCALE) ? iscgraph(c) : iswgraph(c))
+
+#define iswoctal(c)	(((u_char)(c)) >= L'0' && ((u_char)(c)) <= L'7')
+#define iswwhite(c)	(c == L' ' || c == L'\t' || c == L'\n')
+#define iswsafe(c)	(c == L'\b' || c == BELL || c == L'\r')
+#define xtoa(c)		L"0123456789abcdef"[c]
+#define XTOA(c)		L"0123456789ABCDEF"[c]
+
+#define MAXEXTRAS	30
+
+static const wchar_t char_shell[] = L"'`\";&<>()|{}]\\$!^~";
+static const wchar_t char_glob[] = L"*?[#";
+
+/*
+ * On NetBSD and glibc MB_LEN_MAX is currently > 8 which does not fit on any
+ * integer integral type and it is probably wrong, since currently the maximum
+ * number of bytes and character needs is 6. Until this is fixed, the
+ * loops below are using sizeof(uint64_t) - 1 instead of MB_LEN_MAX, and
+ * the assertion is commented out.
+ */
+#if 0
+#ifndef CTASSERT
+#define CTASSERT(x)             _CTASSERT(x, __LINE__)
+#define _CTASSERT(x, y)         __CTASSERT(x, y)
+#define __CTASSERT(x, y)        typedef char __assert ## y[(x) ? 1 : -1]
+#endif
+
+CTASSERT(MB_LEN_MAX <= sizeof(uint64_t));
+#endif
+
+/*
+ * This is do_hvis, for HTTP style (RFC 1808)
+ */
+static wchar_t *
+do_hvis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
+{
+	if (iswalnum(c)
+	    /* safe */
+	    || c == L'$' || c == L'-' || c == L'_' || c == L'.' || c == L'+'
+	    /* extra */
+	    || c == L'!' || c == L'*' || c == L'\'' || c == L'(' || c == L')'
+	    || c == L',')
+		dst = do_svis(dst, c, flags, nextc, extra);
+	else {
+		*dst++ = L'%';
+		*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
+		*dst++ = xtoa((unsigned int)c & 0xf);
+	}
+
+	return dst;
+}
+
+/*
+ * This is do_mvis, for Quoted-Printable MIME (RFC 2045)
+ * NB: No handling of long lines or CRLF.
+ */
+static wchar_t *
+do_mvis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
+{
+	if ((c != L'\n') &&
+	    /* Space at the end of the line */
+	    ((iswspace(c) && (nextc == L'\r' || nextc == L'\n')) ||
+	    /* Out of range */
+	    (!iswspace(c) && (c < 33 || (c > 60 && c < 62) || c > 126)) ||
+	    /* Specific char to be escaped */
+	    wcschr(L"#$@[\\]^`{|}~", c) != NULL)) {
+		*dst++ = L'=';
+		*dst++ = XTOA(((unsigned int)c >> 4) & 0xf);
+		*dst++ = XTOA((unsigned int)c & 0xf);
+	} else
+		dst = do_svis(dst, c, flags, nextc, extra);
+	return dst;
+}
+
+/*
+ * Output single byte of multibyte character.
+ */
+static wchar_t *
+do_mbyte(wchar_t *dst, wint_t c, int flags, wint_t nextc, int iswextra)
+{
+	if (flags & VIS_CSTYLE) {
+		switch (c) {
+		case L'\n':
+			*dst++ = L'\\'; *dst++ = L'n';
+			return dst;
+		case L'\r':
+			*dst++ = L'\\'; *dst++ = L'r';
+			return dst;
+		case L'\b':
+			*dst++ = L'\\'; *dst++ = L'b';
+			return dst;
+		case BELL:
+			*dst++ = L'\\'; *dst++ = L'a';
+			return dst;
+		case L'\v':
+			*dst++ = L'\\'; *dst++ = L'v';
+			return dst;
+		case L'\t':
+			*dst++ = L'\\'; *dst++ = L't';
+			return dst;
+		case L'\f':
+			*dst++ = L'\\'; *dst++ = L'f';
+			return dst;
+		case L' ':
+			*dst++ = L'\\'; *dst++ = L's';
+			return dst;
+		case L'\0':
+			*dst++ = L'\\'; *dst++ = L'0';
+			if (iswoctal(nextc)) {
+				*dst++ = L'0';
+				*dst++ = L'0';
+			}
+			return dst;
+		/* We cannot encode these characters in VIS_CSTYLE
+		 * because they special meaning */
+		case L'n':
+		case L'r':
+		case L'b':
+		case L'a':
+		case L'v':
+		case L't':
+		case L'f':
+		case L's':
+		case L'0':
+		case L'M':
+		case L'^':
+		case L'$': /* vis(1) -l */
+			break;
+		default:
+			if (ISGRAPH(flags, c) && !iswoctal(c)) {
+				*dst++ = L'\\';
+				*dst++ = c;
+				return dst;
+			}
 		}
 	}
+	if (iswextra || ((c & 0177) == L' ') || (flags & VIS_OCTAL)) {
+		*dst++ = L'\\';
+		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + L'0';
+		*dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + L'0';
+		*dst++ =			     (c	      & 07) + L'0';
+	} else {
+		if ((flags & VIS_NOSLASH) == 0)
+			*dst++ = L'\\';
+
+		if (c & 0200) {
+			c &= 0177;
+			*dst++ = L'M';
+		}
+
+		if (iswcntrl(c)) {
+			*dst++ = L'^';
+			if (c == 0177)
+				*dst++ = L'?';
+			else
+				*dst++ = c + L'@';
+		} else {
+			*dst++ = L'-';
+			*dst++ = c;
+		}
+	}
+
+	return dst;
+}
 
-	if ((flag & VIS_GLOB) &&
-	    (c == '*' || c == '?' || c == '[' || c == '#'))
-		;
-	else if (isgraph(c) ||
-	   ((flag & VIS_SP) == 0 && c == ' ') ||
-	   ((flag & VIS_TAB) == 0 && c == '\t') ||
-	   ((flag & VIS_NL) == 0 && c == '\n') ||
-	   ((flag & VIS_SAFE) && (c == '\b' || c == '\007' || c == '\r'))) {
+/*
+ * This is do_vis, the central code of vis.
+ * dst:	      Pointer to the destination buffer
+ * c:	      Character to encode
+ * flags:     Flags word
+ * nextc:     The character following 'c'
+ * extra:     Pointer to the list of extra characters to be
+ *	      backslash-protected.
+ */
+static wchar_t *
+do_svis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
+{
+	int iswextra, i, shft;
+	uint64_t bmsk, wmsk;
+
+	iswextra = wcschr(extra, c) != NULL;
+	if (!iswextra && (ISGRAPH(flags, c) || iswwhite(c) ||
+	    ((flags & VIS_SAFE) && iswsafe(c)))) {
 		*dst++ = c;
-		if (c == '\\' && (flag & VIS_NOSLASH) == 0)
-			*dst++ = '\\';
-		*dst = '\0';
-		return (dst);
+		return dst;
 	}
 
-	if (flag & VIS_CSTYLE) {
-		switch(c) {
-		case '\n':
-			*dst++ = '\\';
-			*dst++ = 'n';
-			goto done;
-		case '\r':
-			*dst++ = '\\';
-			*dst++ = 'r';
-			goto done;
-		case '\b':
-			*dst++ = '\\';
-			*dst++ = 'b';
-			goto done;
-		case '\a':
-			*dst++ = '\\';
-			*dst++ = 'a';
-			goto done;
-		case '\v':
-			*dst++ = '\\';
-			*dst++ = 'v';
-			goto done;
-		case '\t':
-			*dst++ = '\\';
-			*dst++ = 't';
-			goto done;
-		case '\f':
-			*dst++ = '\\';
-			*dst++ = 'f';
-			goto done;
-		case ' ':
-			*dst++ = '\\';
-			*dst++ = 's';
-			goto done;
-		case '\0':
-			*dst++ = '\\';
-			*dst++ = '0';
-			if (isoctal(nextc)) {
-				*dst++ = '0';
-				*dst++ = '0';
-			}
-			goto done;
+	/* See comment in istrsenvisx() output loop, below. */
+	wmsk = 0;
+	for (i = sizeof(wmsk) - 1; i >= 0; i--) {
+		shft = i * NBBY;
+		bmsk = (uint64_t)0xffLL << shft;
+		wmsk |= bmsk;
+		if ((c & wmsk) || i == 0)
+			dst = do_mbyte(dst, (wint_t)(
+			    (uint64_t)(c & bmsk) >> shft),
+			    flags, nextc, iswextra);
+	}
+
+	return dst;
+}
+
+typedef wchar_t *(*visfun_t)(wchar_t *, wint_t, int, wint_t, const wchar_t *);
+
+/*
+ * Return the appropriate encoding function depending on the flags given.
+ */
+static visfun_t
+getvisfun(int flags)
+{
+	if (flags & VIS_HTTPSTYLE)
+		return do_hvis;
+	if (flags & VIS_MIMESTYLE)
+		return do_mvis;
+	return do_svis;
+}
+
+/*
+ * Expand list of extra characters to not visually encode.
+ */
+static wchar_t *
+makeextralist(int flags, const char *src)
+{
+	wchar_t *dst, *d;
+	size_t len;
+	const wchar_t *s;
+
+	len = strlen(src);
+	if ((dst = calloc(len + MAXEXTRAS, sizeof(*dst))) == NULL)
+		return NULL;
+
+	if ((flags & VIS_NOLOCALE) || mbstowcs(dst, src, len) == (size_t)-1) {
+		size_t i;
+		for (i = 0; i < len; i++)
+			dst[i] = (wchar_t)(u_char)src[i];
+		d = dst + len;
+	} else
+		d = dst + wcslen(dst);
+
+	if (flags & VIS_GLOB)
+		for (s = char_glob; *s; *d++ = *s++)
+			continue;
+
+	if (flags & VIS_SHELL)
+		for (s = char_shell; *s; *d++ = *s++)
+			continue;
+
+	if (flags & VIS_SP) *d++ = L' ';
+	if (flags & VIS_TAB) *d++ = L'\t';
+	if (flags & VIS_NL) *d++ = L'\n';
+	if (flags & VIS_DQ) *d++ = L'"';
+	if ((flags & VIS_NOSLASH) == 0) *d++ = L'\\';
+	*d = L'\0';
+
+	return dst;
+}
+
+/*
+ * istrsenvisx()
+ *	The main internal function.
+ *	All user-visible functions call this one.
+ */
+static int
+istrsenvisx(char **mbdstp, size_t *dlen, const char *mbsrc, size_t mblength,
+    int flags, const char *mbextra, int *cerr_ptr)
+{
+	wchar_t *dst, *src, *pdst, *psrc, *start, *extra;
+	size_t len, olen;
+	uint64_t bmsk, wmsk;
+	wint_t c;
+	visfun_t f;
+	int clen = 0, cerr, error = -1, i, shft;
+	char *mbdst, *mdst;
+	ssize_t mbslength, maxolen;
+
+	_DIAGASSERT(mbdstp != NULL);
+	_DIAGASSERT(mbsrc != NULL || mblength == 0);
+	_DIAGASSERT(mbextra != NULL);
+
+	mbslength = (ssize_t)mblength;
+	/*
+	 * When inputing a single character, must also read in the
+	 * next character for nextc, the look-ahead character.
+	 */
+	if (mbslength == 1)
+		mbslength++;
+
+	/*
+	 * Input (mbsrc) is a char string considered to be multibyte
+	 * characters.  The input loop will read this string pulling
+	 * one character, possibly multiple bytes, from mbsrc and
+	 * converting each to wchar_t in src.
+	 *
+	 * The vis conversion will be done using the wide char
+	 * wchar_t string.
+	 *
+	 * This will then be converted back to a multibyte string to
+	 * return to the caller.
+	 */
+
+	/* Allocate space for the wide char strings */
+	psrc = pdst = extra = NULL;
+	mdst = NULL;
+	if ((psrc = calloc(mbslength + 1, sizeof(*psrc))) == NULL)
+		return -1;
+	if ((pdst = calloc((16 * mbslength) + 1, sizeof(*pdst))) == NULL)
+		goto out;
+	if (*mbdstp == NULL) {
+		if ((mdst = calloc((16 * mbslength) + 1, sizeof(*mdst))) == NULL)
+			goto out;
+		*mbdstp = mdst;
+	}
+
+	mbdst = *mbdstp;
+	dst = pdst;
+	src = psrc;
+
+	if (flags & VIS_NOLOCALE) {
+		/* Do one byte at a time conversion */
+		cerr = 1;
+	} else {
+		/* Use caller's multibyte conversion error flag. */
+		cerr = cerr_ptr ? *cerr_ptr : 0;
+	}
+
+	/*
+	 * Input loop.
+	 * Handle up to mblength characters (not bytes).  We do not
+	 * stop at NULs because we may be processing a block of data
+	 * that includes NULs.
+	 */
+	while (mbslength > 0) {
+		/* Convert one multibyte character to wchar_t. */
+		if (!cerr)
+			clen = mbtowc(src, mbsrc, MB_LEN_MAX);
+		if (cerr || clen < 0) {
+			/* Conversion error, process as a byte instead. */
+			*src = (wint_t)(u_char)*mbsrc;
+			clen = 1;
+			cerr = 1;
+		}
+		if (clen == 0) {
+			/*
+			 * NUL in input gives 0 return value. process
+			 * as single NUL byte and keep going.
+			 */
+			clen = 1;
 		}
+		/* Advance buffer character pointer. */
+		src++;
+		/* Advance input pointer by number of bytes read. */
+		mbsrc += clen;
+		/* Decrement input byte count. */
+		mbslength -= clen;
 	}
-	if (((c & 0177) == ' ') || isgraph(c) || (flag & VIS_OCTAL)) {
-		*dst++ = '\\';
-		*dst++ = ((unsigned char)c >> 6 & 07) + '0';
-		*dst++ = ((unsigned char)c >> 3 & 07) + '0';
-		*dst++ = ((unsigned char)c & 07) + '0';
-		goto done;
+	len = src - psrc;
+	src = psrc;
+
+	/*
+	 * In the single character input case, we will have actually
+	 * processed two characters, c and nextc.  Reset len back to
+	 * just a single character.
+	 */
+	if (mblength < len)
+		len = mblength;
+
+	/* Convert extra argument to list of characters for this mode. */
+	extra = makeextralist(flags, mbextra);
+	if (!extra) {
+		if (dlen && *dlen == 0) {
+			errno = ENOSPC;
+			goto out;
+		}
+		*mbdst = '\0';	/* can't create extra, return "" */
+		error = 0;
+		goto out;
 	}
-	if ((flag & VIS_NOSLASH) == 0)
-		*dst++ = '\\';
-	if (c & 0200) {
-		c &= 0177;
-		*dst++ = 'M';
+
+	/* Look up which processing function to call. */
+	f = getvisfun(flags);
+
+	/*
+	 * Main processing loop.
+	 * Call do_Xvis processing function one character at a time
+	 * with next character available for look-ahead.
+	 */
+	for (start = dst; len > 0; len--) {
+		c = *src++;
+		dst = (*f)(dst, c, flags, len >= 1 ? *src : L'\0', extra);
+		if (dst == NULL) {
+			errno = ENOSPC;
+			goto out;
+		}
 	}
-	if (iscntrl(c)) {
-		*dst++ = '^';
-		if (c == 0177)
-			*dst++ = '?';
-		else
-			*dst++ = c + '@';
-	} else {
-		*dst++ = '-';
-		*dst++ = c;
+
+	/* Terminate the string in the buffer. */
+	*dst = L'\0';
+
+	/*
+	 * Output loop.
+	 * Convert wchar_t string back to multibyte output string.
+	 * If we have hit a multi-byte conversion error on input,
+	 * output byte-by-byte here.  Else use wctomb().
+	 */
+	len = wcslen(start);
+	maxolen = dlen ? *dlen : (wcslen(start) * MB_LEN_MAX + 1);
+	olen = 0;
+	for (dst = start; len > 0; len--) {
+		if (!cerr)
+			clen = wctomb(mbdst, *dst);
+		if (cerr || clen < 0) {
+			/*
+			 * Conversion error, process as a byte(s) instead.
+			 * Examine each byte and higher-order bytes for
+			 * data.  E.g.,
+			 *	0x000000000000a264 -> a2 64
+			 *	0x000000001f00a264 -> 1f 00 a2 64
+			 */
+			clen = 0;
+			wmsk = 0;
+			for (i = sizeof(wmsk) - 1; i >= 0; i--) {
+				shft = i * NBBY;
+				bmsk = (uint64_t)0xffLL << shft;
+				wmsk |= bmsk;
+				if ((*dst & wmsk) || i == 0)
+					mbdst[clen++] = (char)(
+					    (uint64_t)(*dst & bmsk) >>
+					    shft);
+			}
+			cerr = 1;
+		}
+		/* If this character would exceed our output limit, stop. */
+		if (olen + clen > (size_t)maxolen)
+			break;
+		/* Advance output pointer by number of bytes written. */
+		mbdst += clen;
+		/* Advance buffer character pointer. */
+		dst++;
+		/* Incrment output character count. */
+		olen += clen;
+	}
+
+	/* Terminate the output string. */
+	*mbdst = '\0';
+
+	if (flags & VIS_NOLOCALE) {
+		/* Pass conversion error flag out. */
+		if (cerr_ptr)
+			*cerr_ptr = cerr;
 	}
-done:
-	*dst = '\0';
-	return (dst);
+
+	free(extra);
+	free(pdst);
+	free(psrc);
+
+	return (int)olen;
+out:
+	free(extra);
+	free(pdst);
+	free(psrc);
+	free(mdst);
+	return error;
 }
 
+static int
+istrsenvisxl(char **mbdstp, size_t *dlen, const char *mbsrc,
+    int flags, const char *mbextra, int *cerr_ptr)
+{
+	return istrsenvisx(mbdstp, dlen, mbsrc,
+	    mbsrc != NULL ? strlen(mbsrc) : 0, flags, mbextra, cerr_ptr);
+}
+
+
 /*
- * strvis, strnvis, strvisx - visually encode characters from src into dst
+ *	The "svis" variants all take an "extra" arg that is a pointer
+ *	to a NUL-terminated list of characters to be encoded, too.
+ *	These functions are useful e. g. to encode strings in such a
+ *	way so that they are not interpreted by a shell.
+ */
+
+char *
+svis(char *mbdst, int c, int flags, int nextc, const char *mbextra)
+{
+	char cc[2];
+	int ret;
+
+	cc[0] = c;
+	cc[1] = nextc;
+
+	ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, mbextra, NULL);
+	if (ret < 0)
+		return NULL;
+	return mbdst + ret;
+}
+
+char *
+snvis(char *mbdst, size_t dlen, int c, int flags, int nextc, const char *mbextra)
+{
+	char cc[2];
+	int ret;
+
+	cc[0] = c;
+	cc[1] = nextc;
+
+	ret = istrsenvisx(&mbdst, &dlen, cc, 1, flags, mbextra, NULL);
+	if (ret < 0)
+		return NULL;
+	return mbdst + ret;
+}
+
+int
+strsvis(char *mbdst, const char *mbsrc, int flags, const char *mbextra)
+{
+	return istrsenvisxl(&mbdst, NULL, mbsrc, flags, mbextra, NULL);
+}
+
+int
+strsnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags, const char *mbextra)
+{
+	return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, mbextra, NULL);
+}
+
+int
+strsvisx(char *mbdst, const char *mbsrc, size_t len, int flags, const char *mbextra)
+{
+	return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, mbextra, NULL);
+}
+
+int
+strsnvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
+    const char *mbextra)
+{
+	return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, mbextra, NULL);
+}
+
+int
+strsenvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
+    const char *mbextra, int *cerr_ptr)
+{
+	return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, mbextra, cerr_ptr);
+}
+
+/*
+ * vis - visually encode characters
+ */
+char *
+vis(char *mbdst, int c, int flags, int nextc)
+{
+	char cc[2];
+	int ret;
+
+	cc[0] = c;
+	cc[1] = nextc;
+
+	ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, "", NULL);
+	if (ret < 0)
+		return NULL;
+	return mbdst + ret;
+}
+
+char *
+nvis(char *mbdst, size_t dlen, int c, int flags, int nextc)
+{
+	char cc[2];
+	int ret;
+
+	cc[0] = c;
+	cc[1] = nextc;
+
+	ret = istrsenvisx(&mbdst, &dlen, cc, 1, flags, "", NULL);
+	if (ret < 0)
+		return NULL;
+	return mbdst + ret;
+}
+
+/*
+ * strvis - visually encode characters from src into dst
  *
  *	Dst must be 4 times the size of src to account for possible
- *	expansion.  The length of dst, not including the trailing NUL,
+ *	expansion.  The length of dst, not including the trailing NULL,
  *	is returned.
- *
- *	Strnvis will write no more than siz-1 bytes (and will NULL terminate).
- *	The number of bytes needed to fully encode the string is returned.
- *
- *	Strvisx encodes exactly len bytes from src into dst.
- *	This is useful for encoding a block of data.
  */
+
 int
-strvis(char *dst, const char *src, int flag)
+strvis(char *mbdst, const char *mbsrc, int flags)
 {
-	char c;
-	char *start;
-
-	for (start = dst; (c = *src); )
-		dst = vis(dst, c, flag, *++src);
-	*dst = '\0';
-	return (dst - start);
+	return istrsenvisxl(&mbdst, NULL, mbsrc, flags, "", NULL);
 }
 
 int
-strnvis(char *dst, const char *src, size_t siz, int flag)
+strnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags)
 {
-	char *start, *end;
-	char tbuf[5];
-	int c, i;
+	return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, "", NULL);
+}
 
-	i = 0;
-	for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
-		if (isvisible(c)) {
-			i = 1;
-			*dst++ = c;
-			if (c == '\\' && (flag & VIS_NOSLASH) == 0) {
-				/* need space for the extra '\\' */
-				if (dst < end)
-					*dst++ = '\\';
-				else {
-					dst--;
-					i = 2;
-					break;
-				}
-			}
-			src++;
-		} else {
-			i = vis(tbuf, c, flag, *++src) - tbuf;
-			if (dst + i <= end) {
-				memcpy(dst, tbuf, i);
-				dst += i;
-			} else {
-				src--;
-				break;
-			}
-		}
-	}
-	if (siz > 0)
-		*dst = '\0';
-	if (dst + i > end) {
-		/* adjust return value for truncation */
-		while ((c = *src))
-			dst += vis(tbuf, c, flag, *++src) - tbuf;
-	}
-	return (dst - start);
+int
+stravis(char **mbdstp, const char *mbsrc, int flags)
+{
+	*mbdstp = NULL;
+	return istrsenvisxl(mbdstp, NULL, mbsrc, flags, "", NULL);
 }
 
+/*
+ * strvisx - visually encode characters from src into dst
+ *
+ *	Dst must be 4 times the size of src to account for possible
+ *	expansion.  The length of dst, not including the trailing NULL,
+ *	is returned.
+ *
+ *	Strvisx encodes exactly len characters from src into dst.
+ *	This is useful for encoding a block of data.
+ */
+
 int
-strvisx(char *dst, const char *src, size_t len, int flag)
+strvisx(char *mbdst, const char *mbsrc, size_t len, int flags)
 {
-	int c;
-	char *start;
+	return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, "", NULL);
+}
 
-	for (start = dst; len > 1; len--) {
-		c = *src;
-		dst = vis(dst, c, flag, *++src);
-	}
-	if (len)
-		dst = vis(dst, *src, flag, '\0');
-	*dst = '\0';
+int
+strnvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags)
+{
+	return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", NULL);
+}
 
-	return (dst - start);
+int
+strenvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
+    int *cerr_ptr)
+{
+	return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", cerr_ptr);
 }
commit 3efad641551f79adbacead4f1d1641e63c2efa05
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 01:56:33 2018 +0200

    Update readpassphrase() from OpenBSD

diff --git a/man/readpassphrase.3bsd b/man/readpassphrase.3bsd
index 9729061..53ad52d 100644
--- a/man/readpassphrase.3bsd
+++ b/man/readpassphrase.3bsd
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: readpassphrase.3,v 1.16 2005/07/22 03:16:58 jaredy Exp $
+.\"	$OpenBSD: readpassphrase.3,v 1.20 2014/03/06 23:03:18 millert Exp $
 .\"
 .\" Copyright (c) 2000, 2002 Todd C. Miller <Todd.Miller at courtesan.com>
 .\"
@@ -18,7 +18,7 @@
 .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
 .\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
 .\"
-.Dd $Mdocdate: May 31 2007 $
+.Dd $Mdocdate: March 6 2014 $
 .Dt READPASSPHRASE 3bsd
 .Os
 .Sh NAME
@@ -55,9 +55,11 @@ Up to
 Any additional
 characters and the terminating newline (or return) character are discarded.
 .Pp
-.Fn readpassphrase
-takes the following optional
-.Fa flags :
+The
+.Fa flags
+argument is the bitwise
+.Tn OR
+of zero or more of the following values:
 .Bd -literal -offset indent
 RPP_ECHO_OFF		turn off echo (default behavior)
 RPP_ECHO_ON		leave echo on
@@ -65,7 +67,7 @@ RPP_REQUIRE_TTY		fail if there is no tty
 RPP_FORCELOWER		force input to lower case
 RPP_FORCEUPPER		force input to upper case
 RPP_SEVENBIT		strip the high bit from input
-RPP_STDIN		force read of passphrase from stdin
+RPP_STDIN		read passphrase from stdin; ignore prompt
 .Ed
 .Pp
 The calling process should zero the passphrase as soon as possible to
@@ -100,7 +102,7 @@ if (compare(transform(passbuf), epass) != 0)
 
 \&...
 
-memset(passbuf, 0, sizeof(passbuf));
+explicit_bzero(passbuf, sizeof(passbuf));
 .Ed
 .Sh ERRORS
 .Bl -tag -width Er
diff --git a/src/readpassphrase.c b/src/readpassphrase.c
index 1f4fe0e..f9f6195 100644
--- a/src/readpassphrase.c
+++ b/src/readpassphrase.c
@@ -1,7 +1,8 @@
-/*	$OpenBSD: readpassphrase.c,v 1.20 2007/10/30 12:03:48 millert Exp $	*/
+/*	$OpenBSD: readpassphrase.c,v 1.26 2016/10/18 12:47:18 millert Exp $	*/
 
 /*
- * Copyright (c) 2000-2002, 2007 Todd C. Miller <Todd.Miller at courtesan.com>
+ * Copyright (c) 2000-2002, 2007, 2010
+ *	Todd C. Miller <Todd.Miller at courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -35,7 +36,7 @@
 #define TCSASOFT 0
 #endif
 
-static volatile sig_atomic_t signo;
+static volatile sig_atomic_t signo[_NSIG];
 
 static void handler(int);
 
@@ -43,7 +44,7 @@ char *
 readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
 {
 	ssize_t nr;
-	int input, output, save_errno;
+	int input, output, save_errno, i, need_restart;
 	char ch, *p, *end;
 	struct termios term, oterm;
 	struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
@@ -56,9 +57,11 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
 	}
 
 restart:
-	signo = 0;
+	for (i = 0; i < _NSIG; i++)
+		signo[i] = 0;
 	nr = -1;
 	save_errno = 0;
+	need_restart = 0;
 	/*
 	 * Read and write to /dev/tty if available.  If not, read from
 	 * stdin and write to stderr unless a tty is required.
@@ -74,24 +77,10 @@ restart:
 	}
 
 	/*
-	 * Catch signals that would otherwise cause the user to end
-	 * up with echo turned off in the shell.  Don't worry about
-	 * things like SIGXCPU and SIGVTALRM for now.
+	 * Turn off echo if possible.
+	 * If we are using a tty but are not the foreground pgrp this will
+	 * generate SIGTTOU, so do it *before* installing the signal handlers.
 	 */
-	sigemptyset(&sa.sa_mask);
-	sa.sa_flags = 0;		/* don't restart system calls */
-	sa.sa_handler = handler;
-	(void)sigaction(SIGALRM, &sa, &savealrm);
-	(void)sigaction(SIGHUP, &sa, &savehup);
-	(void)sigaction(SIGINT, &sa, &saveint);
-	(void)sigaction(SIGPIPE, &sa, &savepipe);
-	(void)sigaction(SIGQUIT, &sa, &savequit);
-	(void)sigaction(SIGTERM, &sa, &saveterm);
-	(void)sigaction(SIGTSTP, &sa, &savetstp);
-	(void)sigaction(SIGTTIN, &sa, &savettin);
-	(void)sigaction(SIGTTOU, &sa, &savettou);
-
-	/* Turn off echo if possible. */
 	if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
 		memcpy(&term, &oterm, sizeof(term));
 		if (!(flags & RPP_ECHO_ON))
@@ -108,36 +97,55 @@ restart:
 		oterm.c_lflag |= ECHO;
 	}
 
-	/* No I/O if we are already backgrounded. */
-	if (signo != SIGTTOU && signo != SIGTTIN) {
-		if (!(flags & RPP_STDIN))
-			(void)write(output, prompt, strlen(prompt));
-		end = buf + bufsiz - 1;
-		p = buf;
-		while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
-			if (p < end) {
-				if ((flags & RPP_SEVENBIT))
-					ch &= 0x7f;
-				if (isalpha(ch)) {
-					if ((flags & RPP_FORCELOWER))
-						ch = (char)tolower(ch);
-					if ((flags & RPP_FORCEUPPER))
-						ch = (char)toupper(ch);
-				}
-				*p++ = ch;
+	/*
+	 * Catch signals that would otherwise cause the user to end
+	 * up with echo turned off in the shell.  Don't worry about
+	 * things like SIGXCPU and SIGVTALRM for now.
+	 */
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = 0;		/* don't restart system calls */
+	sa.sa_handler = handler;
+	(void)sigaction(SIGALRM, &sa, &savealrm);
+	(void)sigaction(SIGHUP, &sa, &savehup);
+	(void)sigaction(SIGINT, &sa, &saveint);
+	(void)sigaction(SIGPIPE, &sa, &savepipe);
+	(void)sigaction(SIGQUIT, &sa, &savequit);
+	(void)sigaction(SIGTERM, &sa, &saveterm);
+	(void)sigaction(SIGTSTP, &sa, &savetstp);
+	(void)sigaction(SIGTTIN, &sa, &savettin);
+	(void)sigaction(SIGTTOU, &sa, &savettou);
+
+	if (!(flags & RPP_STDIN))
+		(void)write(output, prompt, strlen(prompt));
+	end = buf + bufsiz - 1;
+	p = buf;
+	while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
+		if (p < end) {
+			if ((flags & RPP_SEVENBIT))
+				ch &= 0x7f;
+			if (isalpha((unsigned char)ch)) {
+				if ((flags & RPP_FORCELOWER))
+					ch = (char)tolower((unsigned char)ch);
+				if ((flags & RPP_FORCEUPPER))
+					ch = (char)toupper((unsigned char)ch);
 			}
+			*p++ = ch;
 		}
-		*p = '\0';
-		save_errno = errno;
-		if (!(term.c_lflag & ECHO))
-			(void)write(output, "\n", 1);
 	}
+	*p = '\0';
+	save_errno = errno;
+	if (!(term.c_lflag & ECHO))
+		(void)write(output, "\n", 1);
 
 	/* Restore old terminal settings and signals. */
 	if (memcmp(&term, &oterm, sizeof(term)) != 0) {
+		const int sigttou = signo[SIGTTOU];
+
+		/* Ignore SIGTTOU generated when we are not the fg pgrp. */
 		while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 &&
-		    errno == EINTR)
+		    errno == EINTR && !signo[SIGTTOU])
 			continue;
+		signo[SIGTTOU] = sigttou;
 	}
 	(void)sigaction(SIGALRM, &savealrm, NULL);
 	(void)sigaction(SIGHUP, &savehup, NULL);
@@ -155,15 +163,19 @@ restart:
 	 * If we were interrupted by a signal, resend it to ourselves
 	 * now that we have restored the signal handlers.
 	 */
-	if (signo) {
-		kill(getpid(), signo);
-		switch (signo) {
-		case SIGTSTP:
-		case SIGTTIN:
-		case SIGTTOU:
-			goto restart;
+	for (i = 0; i < _NSIG; i++) {
+		if (signo[i]) {
+			kill(getpid(), i);
+			switch (i) {
+			case SIGTSTP:
+			case SIGTTIN:
+			case SIGTTOU:
+				need_restart = 1;
+			}
 		}
 	}
+	if (need_restart)
+		goto restart;
 
 	if (save_errno)
 		errno = save_errno;
@@ -183,5 +195,5 @@ getpass(const char *prompt)
 static void handler(int s)
 {
 
-	signo = s;
+	signo[s] = 1;
 }
commit a6f407ab0de24c725cb8c66a06ab30527e5c33f2
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 01:34:17 2018 +0200

    Update heapsort() from OpenBSD

diff --git a/src/heapsort.c b/src/heapsort.c
index 72dbcbc..a2b7bd6 100644
--- a/src/heapsort.c
+++ b/src/heapsort.c
@@ -1,3 +1,4 @@
+/*	$OpenBSD: heapsort.c,v 1.11 2017/05/20 12:48:56 millert Exp $ */
 /*-
  * Copyright (c) 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -64,7 +65,7 @@
  * Build the list into a heap, where a heap is defined such that for
  * the records K1 ... KN, Kj/2 >= Kj for 1 <= j/2 <= j <= N.
  *
- * There two cases.  If j == nmemb, select largest of Ki and Kj.  If
+ * There are two cases.  If j == nmemb, select largest of Ki and Kj.  If
  * j < nmemb, select largest of Ki, Kj and Kj+1.
  */
 #define CREATE(initval, nmemb, par_i, child_i, par, child, size, count, tmp) { \
@@ -86,12 +87,12 @@
  * Select the top of the heap and 'heapify'.  Since by far the most expensive
  * action is the call to the compar function, a considerable optimization
  * in the average case can be achieved due to the fact that k, the displaced
- * elememt, is usually quite small, so it would be preferable to first
+ * element, is usually quite small, so it would be preferable to first
  * heapify, always maintaining the invariant that the larger child is copied
  * over its parent's record.
  *
  * Then, starting from the *bottom* of the heap, finding k's correct place,
- * again maintianing the invariant.  As a result of the invariant no element
+ * again maintaining the invariant.  As a result of the invariant no element
  * is 'lost' when k is assigned its correct place in the heap.
  *
  * The time savings from this optimization are on the order of 15-20% for the
@@ -131,7 +132,7 @@
  */
 int
 heapsort(void *vbase, size_t nmemb, size_t size,
-	int (*compar)(const void *, const void *))
+    int (*compar)(const void *, const void *))
 {
 	size_t cnt, i, j, l;
 	char tmp, *tmp1, *tmp2;
commit 21edbb4f22935f2c0484db6147dfd9825adb8ec0
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 00:31:15 2018 +0200

    Update fmtcheck() from NetBSD

diff --git a/man/fmtcheck.3bsd b/man/fmtcheck.3bsd
index 2f06f6f..886ecb9 100644
--- a/man/fmtcheck.3bsd
+++ b/man/fmtcheck.3bsd
@@ -1,3 +1,5 @@
+.\"	$NetBSD: fmtcheck.3,v 1.8 2014/06/14 08:18:24 apb Exp $
+.\"
 .\" Copyright (c) 2000 The NetBSD Foundation, Inc.
 .\" All rights reserved.
 .\"
@@ -24,15 +26,12 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $FreeBSD: /repoman/r/ncvs/src/lib/libc/gen/fmtcheck.3,v 1.9 2004/07/02 23:52:10 ru Exp $
-.Dd October 16, 2002
-.Os
+.Dd June 14, 2014
 .Dt FMTCHECK 3bsd
+.Os
 .Sh NAME
 .Nm fmtcheck
-.Nd sanitizes user-supplied
-.Xr printf 3 Ns -style
-format string
+.Nd sanitizes user-supplied printf(3)-style format string
 .Sh LIBRARY
 .ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
 .Lb libbsd
@@ -45,8 +44,8 @@ for include usage.)
 .Fn fmtcheck "const char *fmt_suspect" "const char *fmt_default"
 .Sh DESCRIPTION
 The
-.Fn fmtcheck
-scans
+.Nm
+function scans
 .Fa fmt_suspect
 and
 .Fa fmt_default
@@ -60,55 +59,45 @@ is a valid format string.
 .Pp
 The
 .Xr printf 3
-family of functions cannot verify the types of arguments that they are
+family of functions can not verify the types of arguments that they are
 passed at run-time.
 In some cases, like
 .Xr catgets 3 ,
 it is useful or necessary to use a user-supplied format string with no
-guarantee that the format string matches the specified arguments.
+guarantee that the format string matches the specified parameters.
 .Pp
 The
-.Fn fmtcheck
-was designed to be used in these cases, as in:
+.Nm
+function was designed to be used in these cases, as in:
 .Bd -literal -offset indent
 printf(fmtcheck(user_format, standard_format), arg1, arg2);
 .Ed
 .Pp
-In the check, field widths, fillers, precisions, etc.\& are ignored (unless
+In the check, field widths, fillers, precisions, etc. are ignored (unless
 the field width or precision is an asterisk
 .Ql *
 instead of a digit string).
-Also, any text other than the format specifiers
-is completely ignored.
+Also, any text other than the format specifiers is completely ignored.
+.Pp
+Note that the formats may be quite different as long as they accept the
+same parameters.
+For example, "%ld %o %30s %#llx %-10.*e %n" is
+compatible with "This number %lu %d%% and string %s has %qd numbers
+and %.*g floats (%n)."
+However, "%o" is not equivalent to "%lx" because
+the first requires an integer and the second requires a long,
+and "%p" is not equivalent to "%lu" because
+the first requires a pointer and the second requires a long.
 .Sh RETURN VALUES
 If
 .Fa fmt_suspect
 is a valid format and consumes the same argument types as
 .Fa fmt_default ,
 then the
-.Fn fmtcheck
-will return
+.Nm
+function will return
 .Fa fmt_suspect .
 Otherwise, it will return
 .Fa fmt_default .
-.Sh SECURITY CONSIDERATIONS
-Note that the formats may be quite different as long as they accept the
-same arguments.
-For example,
-.Qq Li "%p %o %30s %#llx %-10.*e %n"
-is compatible with
-.Qq Li "This number %lu %d%% and string %s has %qd numbers and %.*g floats (%n)" .
-However,
-.Qq Li %o
-is not equivalent to
-.Qq Li %lx
-because
-the first requires an integer and the second requires a long.
 .Sh SEE ALSO
 .Xr printf 3
-.Sh BUGS
-The
-.Fn fmtcheck
-function does not understand all of the conversions that
-.Xr printf 3
-does.
diff --git a/src/fmtcheck.c b/src/fmtcheck.c
index 7497257..e882497 100644
--- a/src/fmtcheck.c
+++ b/src/fmtcheck.c
@@ -1,3 +1,5 @@
+/*	$NetBSD: fmtcheck.c,v 1.16 2017/12/13 06:43:45 rin Exp $	*/
+
 /*-
  * Copyright (c) 2000 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -24,9 +26,6 @@
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
- *
- *	from NetBSD: fmtcheck.c,v 1.2 2000/11/01 01:17:20 briggs Exp
- *	from FreeBSD: fmtcheck.c,v 1.8 2005/03/21 08:00:55 das Exp
  */
 
 #include <sys/cdefs.h>
@@ -35,27 +34,33 @@
 #include <string.h>
 #include <ctype.h>
 
-/* __weak_reference(__fmtcheck, fmtcheck); */
+#ifdef __weak_alias
+__weak_alias(fmtcheck,__fmtcheck)
+#endif
 
 enum __e_fmtcheck_types {
 	FMTCHECK_START,
 	FMTCHECK_SHORT,
 	FMTCHECK_INT,
+	FMTCHECK_WINTT,
 	FMTCHECK_LONG,
 	FMTCHECK_QUAD,
+	FMTCHECK_INTMAXT,
 	FMTCHECK_PTRDIFFT,
 	FMTCHECK_SIZET,
+	FMTCHECK_POINTER,
+	FMTCHECK_CHARPOINTER,
 	FMTCHECK_SHORTPOINTER,
 	FMTCHECK_INTPOINTER,
 	FMTCHECK_LONGPOINTER,
 	FMTCHECK_QUADPOINTER,
+	FMTCHECK_INTMAXTPOINTER,
 	FMTCHECK_PTRDIFFTPOINTER,
 	FMTCHECK_SIZETPOINTER,
-#ifndef NO_FLOATING_POINT
 	FMTCHECK_DOUBLE,
 	FMTCHECK_LONGDOUBLE,
-#endif
 	FMTCHECK_STRING,
+	FMTCHECK_WSTRING,
 	FMTCHECK_WIDTH,
 	FMTCHECK_PRECISION,
 	FMTCHECK_DONE,
@@ -63,6 +68,18 @@ enum __e_fmtcheck_types {
 };
 typedef enum __e_fmtcheck_types EFT;
 
+enum e_modifier {
+	MOD_NONE,
+	MOD_CHAR,
+	MOD_SHORT,
+	MOD_LONG,
+	MOD_QUAD,
+	MOD_INTMAXT,
+	MOD_LONGDOUBLE,
+	MOD_PTRDIFFT,
+	MOD_SIZET,
+};
+
 #define RETURN(pf,f,r) do { \
 			*(pf) = (f); \
 			return r; \
@@ -71,103 +88,168 @@ typedef enum __e_fmtcheck_types EFT;
 static EFT
 get_next_format_from_precision(const char **pf)
 {
-	int		sh, lg, quad, longdouble, ptrdifft, sizet;
+	enum e_modifier	modifier;
 	const char	*f;
 
-	sh = lg = quad = longdouble = ptrdifft = sizet = 0;
-
 	f = *pf;
 	switch (*f) {
 	case 'h':
 		f++;
-		sh = 1;
+		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
+		if (*f == 'h') {
+			f++;
+			modifier = MOD_CHAR;
+		} else {
+			modifier = MOD_SHORT;
+		}
+		break;
+	case 'j':
+		f++;
+		modifier = MOD_INTMAXT;
 		break;
 	case 'l':
 		f++;
 		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
 		if (*f == 'l') {
 			f++;
-			quad = 1;
+			modifier = MOD_QUAD;
 		} else {
-			lg = 1;
+			modifier = MOD_LONG;
 		}
 		break;
 	case 'q':
 		f++;
-		quad = 1;
+		modifier = MOD_QUAD;
 		break;
 	case 't':
 		f++;
-		ptrdifft = 1;
+		modifier = MOD_PTRDIFFT;
 		break;
 	case 'z':
 		f++;
-		sizet = 1;
+		modifier = MOD_SIZET;
 		break;
 	case 'L':
 		f++;
-		longdouble = 1;
+		modifier = MOD_LONGDOUBLE;
 		break;
+#ifdef WIN32
+	case 'I':
+		f++;
+		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
+		if (*f == '3' && f[1] == '2') {
+			f += 2;
+			modifier = MOD_NONE;
+		} else if (*f == '6' && f[1] == '4') {
+			f += 2;
+			modifier = MOD_QUAD;
+		}
+		else {
+#ifdef _WIN64
+			modifier = MOD_QUAD;
+#else
+			modifier = MOD_NONE;
+#endif
+		}
+		break;
+#endif
 	default:
+		modifier = MOD_NONE;
 		break;
 	}
 	if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
 	if (strchr("diouxX", *f)) {
-		if (longdouble)
-			RETURN(pf,f,FMTCHECK_UNKNOWN);
-		if (lg)
+		switch (modifier) {
+		case MOD_LONG:
 			RETURN(pf,f,FMTCHECK_LONG);
-		if (quad)
+		case MOD_QUAD:
 			RETURN(pf,f,FMTCHECK_QUAD);
-		if (ptrdifft)
+		case MOD_INTMAXT:
+			RETURN(pf,f,FMTCHECK_INTMAXT);
+		case MOD_PTRDIFFT:
 			RETURN(pf,f,FMTCHECK_PTRDIFFT);
-		if (sizet)
+		case MOD_SIZET:
 			RETURN(pf,f,FMTCHECK_SIZET);
-		RETURN(pf,f,FMTCHECK_INT);
+		case MOD_CHAR:
+		case MOD_SHORT:
+		case MOD_NONE:
+			RETURN(pf,f,FMTCHECK_INT);
+		default:
+			RETURN(pf,f,FMTCHECK_UNKNOWN);
+		}
 	}
 	if (*f == 'n') {
-		if (longdouble)
-			RETURN(pf,f,FMTCHECK_UNKNOWN);
-		if (sh)
+		switch (modifier) {
+		case MOD_CHAR:
+			RETURN(pf,f,FMTCHECK_CHARPOINTER);
+		case MOD_SHORT:
 			RETURN(pf,f,FMTCHECK_SHORTPOINTER);
-		if (lg)
+		case MOD_LONG:
 			RETURN(pf,f,FMTCHECK_LONGPOINTER);
-		if (quad)
+		case MOD_QUAD:
 			RETURN(pf,f,FMTCHECK_QUADPOINTER);
-		if (ptrdifft)
+		case MOD_INTMAXT:
+			RETURN(pf,f,FMTCHECK_INTMAXTPOINTER);
+		case MOD_PTRDIFFT:
 			RETURN(pf,f,FMTCHECK_PTRDIFFTPOINTER);
-		if (sizet)
+		case MOD_SIZET:
 			RETURN(pf,f,FMTCHECK_SIZETPOINTER);
-		RETURN(pf,f,FMTCHECK_INTPOINTER);
+		case MOD_NONE:
+			RETURN(pf,f,FMTCHECK_INTPOINTER);
+		default:
+			RETURN(pf,f,FMTCHECK_UNKNOWN);
+		}
 	}
 	if (strchr("DOU", *f)) {
-		if (sh + lg + quad + longdouble + ptrdifft + sizet)
+		if (modifier != MOD_NONE)
 			RETURN(pf,f,FMTCHECK_UNKNOWN);
 		RETURN(pf,f,FMTCHECK_LONG);
 	}
-#ifndef NO_FLOATING_POINT
 	if (strchr("aAeEfFgG", *f)) {
-		if (longdouble)
+		switch (modifier) {
+		case MOD_LONGDOUBLE:
 			RETURN(pf,f,FMTCHECK_LONGDOUBLE);
-		if (sh + lg + quad + ptrdifft + sizet)
+		case MOD_LONG:
+		case MOD_NONE:
+			RETURN(pf,f,FMTCHECK_DOUBLE);
+		default:
 			RETURN(pf,f,FMTCHECK_UNKNOWN);
-		RETURN(pf,f,FMTCHECK_DOUBLE);
+		}
 	}
-#endif
 	if (*f == 'c') {
-		if (sh + lg + quad + longdouble + ptrdifft + sizet)
+		switch (modifier) {
+		case MOD_LONG:
+			RETURN(pf,f,FMTCHECK_WINTT);
+		case MOD_NONE:
+			RETURN(pf,f,FMTCHECK_INT);
+		default:
+			RETURN(pf,f,FMTCHECK_UNKNOWN);
+		}
+	}
+	if (*f == 'C') {
+		if (modifier != MOD_NONE)
 			RETURN(pf,f,FMTCHECK_UNKNOWN);
-		RETURN(pf,f,FMTCHECK_INT);
+		RETURN(pf,f,FMTCHECK_WINTT);
 	}
 	if (*f == 's') {
-		if (sh + lg + quad + longdouble + ptrdifft + sizet)
+		switch (modifier) {
+		case MOD_LONG:
+			RETURN(pf,f,FMTCHECK_WSTRING);
+		case MOD_NONE:
+			RETURN(pf,f,FMTCHECK_STRING);
+		default:
 			RETURN(pf,f,FMTCHECK_UNKNOWN);
-		RETURN(pf,f,FMTCHECK_STRING);
+		}
+	}
+	if (*f == 'S') {
+		if (modifier != MOD_NONE)
+			RETURN(pf,f,FMTCHECK_UNKNOWN);
+		RETURN(pf,f,FMTCHECK_WSTRING);
 	}
 	if (*f == 'p') {
-		if (sh + lg + quad + longdouble + ptrdifft + sizet)
+		if (modifier != MOD_NONE)
 			RETURN(pf,f,FMTCHECK_UNKNOWN);
-		RETURN(pf,f,FMTCHECK_LONG);
+		RETURN(pf,f,FMTCHECK_POINTER);
 	}
 	RETURN(pf,f,FMTCHECK_UNKNOWN);
 	/*NOTREACHED*/
@@ -185,7 +267,7 @@ get_next_format_from_width(const char **pf)
 			RETURN(pf,f,FMTCHECK_PRECISION);
 		}
 		/* eat any precision (empty is allowed) */
-		while (isdigit(*f)) f++;
+		while (isdigit((unsigned char)*f)) f++;
 		if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
 	}
 	RETURN(pf,f,get_next_format_from_precision(pf));
@@ -229,7 +311,7 @@ get_next_format(const char **pf, EFT eft)
 		RETURN(pf,f,FMTCHECK_WIDTH);
 	}
 	/* eat any width */
-	while (isdigit(*f)) f++;
+	while (isdigit((unsigned char)*f)) f++;
 	if (!*f) {
 		RETURN(pf,f,FMTCHECK_UNKNOWN);
 	}
@@ -238,7 +320,7 @@ get_next_format(const char **pf, EFT eft)
 	/*NOTREACHED*/
 }
 
-__const char *
+const char *
 fmtcheck(const char *f1, const char *f2)
 {
 	const char	*f1p, *f2p;
commit e3979d1a7ce2777ea959f3361573a10af90db63d
Author: Guillem Jover <guillem at hadrons.org>
Date:   Sun May 20 19:22:16 2018 +0200

    Update humanize_number() from FreeBSD
    
    Implements HN_IEC_PREFIXES.

diff --git a/COPYING b/COPYING
index b768093..c38a56b 100644
--- a/COPYING
+++ b/COPYING
@@ -273,6 +273,7 @@ Files:
  src/stringlist.c
 Copyright:
  Copyright © 1994, 1997-2000, 2002, 2008, 2010 The NetBSD Foundation, Inc.
+ Copyright © 2013 John-Mark Gurney <jmg at FreeBSD.org>
  All rights reserved.
  .
  Some code was contributed to The NetBSD Foundation by Allen Briggs.
diff --git a/include/bsd/libutil.h b/include/bsd/libutil.h
index e4efd6b..9bdcc7f 100644
--- a/include/bsd/libutil.h
+++ b/include/bsd/libutil.h
@@ -67,14 +67,16 @@ int pidfile_remove(struct pidfh *pfh);
 char   *fparseln(FILE *, size_t *, size_t *, const char[3], int);
 __END_DECLS
 
-/* humanize_number(3) */
-#define HN_DECIMAL              0x01
-#define HN_NOSPACE              0x02
-#define HN_B                    0x04
-#define HN_DIVISOR_1000         0x08
+/* Values for humanize_number(3)'s flags parameter. */
+#define HN_DECIMAL		0x01
+#define HN_NOSPACE		0x02
+#define HN_B			0x04
+#define HN_DIVISOR_1000		0x08
+#define HN_IEC_PREFIXES		0x10
 
-#define HN_GETSCALE             0x10
-#define HN_AUTOSCALE            0x20
+/* Values for humanize_number(3)'s scale parameter. */
+#define HN_GETSCALE		0x10
+#define HN_AUTOSCALE		0x20
 
 /*
  * fparseln() specific operation flags.
diff --git a/src/humanize_number.c b/src/humanize_number.c
index f8cf633..e2e4703 100644
--- a/src/humanize_number.c
+++ b/src/humanize_number.c
@@ -1,7 +1,10 @@
 /*	$NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $	*/
 
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
  * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
+ * Copyright 2013 John-Mark Gurney <jmg at FreeBSD.org>
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -31,6 +34,7 @@
  */
 
 #include <sys/cdefs.h>
+#include <sys/types.h>
 #include <assert.h>
 #include <inttypes.h>
 #include <stdio.h>
@@ -39,58 +43,81 @@
 #include <locale.h>
 #include <libutil.h>
 
+static const int maxscale = 6;
+
 int
-humanize_number(char *buf, size_t len, int64_t bytes,
+humanize_number(char *buf, size_t len, int64_t quotient,
     const char *suffix, int scale, int flags)
 {
 	const char *prefixes, *sep;
-	int	b, i, r, maxscale, s1, s2, sign;
+	int	i, r, remainder, s1, s2, sign;
+	int	divisordeccut;
 	int64_t	divisor, max;
 	size_t	baselen;
 
-	assert(buf != NULL);
-	assert(suffix != NULL);
-	assert(scale >= 0);
+	/* Since so many callers don't check -1, NUL terminate the buffer */
+	if (len > 0)
+		buf[0] = '\0';
 
-	if (flags & HN_DIVISOR_1000) {
-		/* SI for decimal multiplies */
-		divisor = 1000;
-		if (flags & HN_B)
-			prefixes = "B\0k\0M\0G\0T\0P\0E";
-		else
-			prefixes = "\0\0k\0M\0G\0T\0P\0E";
-	} else {
+	/* validate args */
+	if (buf == NULL || suffix == NULL)
+		return (-1);
+	if (scale < 0)
+		return (-1);
+	else if (scale > maxscale &&
+	    ((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0))
+		return (-1);
+	if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES))
+		return (-1);
+
+	/* setup parameters */
+	remainder = 0;
+
+	if (flags & HN_IEC_PREFIXES) {
+		baselen = 2;
 		/*
-		 * binary multiplies
-		 * XXX IEC 60027-2 recommends Ki, Mi, Gi...
+		 * Use the prefixes for power of two recommended by
+		 * the International Electrotechnical Commission
+		 * (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).
+		 *
+		 * HN_IEC_PREFIXES implies a divisor of 1024 here
+		 * (use of HN_DIVISOR_1000 would have triggered
+		 * an assertion earlier).
 		 */
 		divisor = 1024;
+		divisordeccut = 973;	/* ceil(.95 * 1024) */
 		if (flags & HN_B)
-			prefixes = "B\0K\0M\0G\0T\0P\0E";
+			prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
 		else
-			prefixes = "\0\0K\0M\0G\0T\0P\0E";
+			prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
+	} else {
+		baselen = 1;
+		if (flags & HN_DIVISOR_1000) {
+			divisor = 1000;
+			divisordeccut = 950;
+			if (flags & HN_B)
+				prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
+			else
+				prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
+		} else {
+			divisor = 1024;
+			divisordeccut = 973;	/* ceil(.95 * 1024) */
+			if (flags & HN_B)
+				prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
+			else
+				prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
+		}
 	}
 
-#define	SCALE2PREFIX(scale)	(&prefixes[(scale) << 1])
-	maxscale = 7;
-
-	if (scale >= maxscale &&
-	    (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0)
-		return (-1);
-
-	if (buf == NULL || suffix == NULL)
-		return (-1);
+#define	SCALE2PREFIX(scale)	(&prefixes[(scale) * 3])
 
-	if (len > 0)
-		buf[0] = '\0';
-	if (bytes < 0) {
+	if (quotient < 0) {
 		sign = -1;
-		bytes *= -100;
-		baselen = 3;		/* sign, digit, prefix */
+		quotient = -quotient;
+		baselen += 2;		/* sign, digit */
 	} else {
 		sign = 1;
-		bytes *= 100;
-		baselen = 2;		/* digit, prefix */
+		baselen += 1;		/* digit */
 	}
 	if (flags & HN_NOSPACE)
 		sep = "";
@@ -106,7 +133,7 @@ humanize_number(char *buf, size_t len, int64_t bytes,
 
 	if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
 		/* See if there is additional columns can be used. */
-		for (max = 100, i = len - baselen; i-- > 0;)
+		for (max = 1, i = len - baselen; i-- > 0;)
 			max *= 10;
 
 		/*
@@ -114,29 +141,39 @@ humanize_number(char *buf, size_t len, int64_t bytes,
 		 * If there will be an overflow by the rounding below,
 		 * divide once more.
 		 */
-		for (i = 0; bytes >= max - 50 && i < maxscale; i++)
-			bytes /= divisor;
+		for (i = 0;
+		    (quotient >= max || (quotient == max - 1 &&
+		    (remainder >= divisordeccut || remainder >=
+		    divisor / 2))) && i < maxscale; i++) {
+			remainder = quotient % divisor;
+			quotient /= divisor;
+		}
 
 		if (scale & HN_GETSCALE)
 			return (i);
-	} else
-		for (i = 0; i < scale && i < maxscale; i++)
-			bytes /= divisor;
+	} else {
+		for (i = 0; i < scale && i < maxscale; i++) {
+			remainder = quotient % divisor;
+			quotient /= divisor;
+		}
+	}
 
 	/* If a value <= 9.9 after rounding and ... */
-	if (bytes < 995 && i > 0 && flags & HN_DECIMAL) {
-		/* baselen + \0 + .N */
-		if (len < baselen + 1 + 2)
-			return (-1);
-		b = ((int)bytes + 5) / 10;
-		s1 = b / 10;
-		s2 = b % 10;
+	/*
+	 * XXX - should we make sure there is enough space for the decimal
+	 * place and if not, don't do HN_DECIMAL?
+	 */
+	if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) &&
+	    i > 0 && flags & HN_DECIMAL) {
+		s1 = (int)quotient + ((remainder * 10 + divisor / 2) /
+		    divisor / 10);
+		s2 = ((remainder * 10 + divisor / 2) / divisor) % 10;
 		r = snprintf(buf, len, "%d%s%d%s%s%s",
 		    sign * s1, localeconv()->decimal_point, s2,
 		    sep, SCALE2PREFIX(i), suffix);
 	} else
 		r = snprintf(buf, len, "%" PRId64 "%s%s%s",
-		    sign * ((bytes + 50) / 100),
+		    sign * (quotient + (remainder + divisor / 2) / divisor),
 		    sep, SCALE2PREFIX(i), suffix);
 
 	return (r);
commit facbddb652eba99c9334e4e390b9d829c590d4c2
Author: Guillem Jover <guillem at hadrons.org>
Date:   Sun May 20 19:18:18 2018 +0200

    Update pidfile module from FreeBSD
    
    Use EINVAL instead of EDOOFUS. Add a missing synopsis for
    pidfile_fileno() in the man page. Move the definition of struct pidfh
    from libutil.h into pidfile.c following upstream change.

diff --git a/include/bsd/libutil.h b/include/bsd/libutil.h
index 2c4c731..e4efd6b 100644
--- a/include/bsd/libutil.h
+++ b/include/bsd/libutil.h
@@ -48,13 +48,7 @@
 #include <stdint.h>
 #include <stdio.h>
 
-/* for pidfile.c */
-struct pidfh {
-	int	pf_fd;
-	char	*pf_path;
-	dev_t	pf_dev;
-	ino_t	pf_ino;
-};
+struct pidfh;
 
 __BEGIN_DECLS
 int humanize_number(char *buf, size_t len, int64_t bytes,
@@ -65,6 +59,7 @@ int flopen(const char *_path, int _flags, ...);
 int flopenat(int dirfd, const char *path, int flags, ...);
 
 struct pidfh *pidfile_open(const char *path, mode_t mode, pid_t *pidptr);
+int pidfile_fileno(const struct pidfh *pfh);
 int pidfile_write(struct pidfh *pfh);
 int pidfile_close(struct pidfh *pfh);
 int pidfile_remove(struct pidfh *pfh);
diff --git a/man/pidfile.3bsd b/man/pidfile.3bsd
index e80d4e0..e8974ae 100644
--- a/man/pidfile.3bsd
+++ b/man/pidfile.3bsd
@@ -24,15 +24,16 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd October 20, 2008
+.Dd February 8, 2012
 .Dt PIDFILE 3bsd
 .Os
 .Sh NAME
 .Nm pidfile_open ,
 .Nm pidfile_write ,
 .Nm pidfile_close ,
-.Nm pidfile_remove
-.Nd library for PID files handling
+.Nm pidfile_remove ,
+.Nm pidfile_fileno
+.Nd "library for PID files handling"
 .Sh LIBRARY
 .ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
 .Lb libbsd
@@ -49,6 +50,8 @@ for include usage.)
 .Fn pidfile_close "struct pidfh *pfh"
 .Ft int
 .Fn pidfile_remove "struct pidfh *pfh"
+.Ft int
+.Fn pidfile_fileno "struct pidfh *pfh"
 .Sh DESCRIPTION
 The
 .Nm pidfile
@@ -62,11 +65,14 @@ The
 function opens (or creates) a file specified by the
 .Fa path
 argument and locks it.
-If a file can not be locked, a PID of an already running daemon is returned in
-the
+If
 .Fa pidptr
-argument (if it is not
-.Dv NULL ) .
+argument is not
+.Dv NULL
+and file can not be locked, the function will use it to store a PID of an
+already running daemon or
+.Li -1
+in case daemon did not write its PID yet.
 The function does not write process' PID into the file here, so it can be
 used before
 .Fn fork Ns ing
@@ -77,10 +83,16 @@ argument is
 .Dv NULL ,
 .Pa /var/run/ Ns Ao Va progname Ac Ns Pa .pid
 file will be used.
+The
+.Fn pidfile_open
+function sets the O_CLOEXEC close-on-exec flag when opening the pidfile.
 .Pp
 The
 .Fn pidfile_write
 function writes process' PID into a previously opened file.
+The file is truncated before write, so calling the
+.Fn pidfile_write
+function multiple times is supported.
 .Pp
 The
 .Fn pidfile_close
@@ -92,6 +104,10 @@ to start a child process.
 The
 .Fn pidfile_remove
 function closes and removes a pidfile.
+.Pp
+The
+.Fn pidfile_fileno
+function returns the file descriptor for the open pidfile.
 .Sh RETURN VALUES
 The
 .Fn pidfile_open
@@ -105,15 +121,27 @@ If an error occurs,
 will be set.
 .Pp
 .Rv -std pidfile_write pidfile_close pidfile_remove
+.Pp
+The
+.Fn pidfile_fileno
+function returns the low-level file descriptor.
+It returns
+.Li -1
+and sets
+.Va errno
+if a NULL
+.Vt pidfh
+is specified, or if the pidfile is no longer open.
 .Sh EXAMPLES
 The following example shows in which order these functions should be used.
 Note that it is safe to pass
 .Dv NULL
 to
 .Fn pidfile_write ,
-.Fn pidfile_remove
-and
+.Fn pidfile_remove ,
 .Fn pidfile_close
+and
+.Fn pidfile_fileno
 functions.
 .Bd -literal
 struct pidfh *pfh;
@@ -127,6 +155,11 @@ if (pfh == NULL) {
 	}
 	/* If we cannot create pidfile from other reasons, only warn. */
 	warn("Cannot open or create pidfile");
+	/*
+	 * Even though pfh is NULL we can continue, as the other pidfile_*
+	 * function can handle such situation by doing nothing except setting
+	 * errno to EINVAL.
+	 */
 }
 
 if (daemon(0, 0) == \-1) {
@@ -165,16 +198,18 @@ function will fail if:
 .It Bq Er EEXIST
 Some process already holds the lock on the given pidfile, meaning that a
 daemon is already running.
+If
+.Fa pidptr
+argument is not
+.Dv NULL
+the function will use it to store a PID of an already running daemon or
+.Li -1
+in case daemon did not write its PID yet.
 .It Bq Er ENAMETOOLONG
 Specified pidfile's name is too long.
 .It Bq Er EINVAL
 Some process already holds the lock on the given pidfile, but PID read
 from there is invalid.
-.It Bq Er EAGAIN
-Some process already holds the lock on the given pidfile, but the file
-is truncated.
-Most likely, the existing daemon is writing new PID into
-the file.
 .El
 .Pp
 The
@@ -242,6 +277,16 @@ and
 system calls and the
 .Xr flopen 3bsd
 library function.
+.Pp
+The
+.Fn pidfile_fileno
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EINVAL
+Improper function use.
+Probably called not from the process which used
+.Fn pidfile_open .
+.El
 .Sh SEE ALSO
 .Xr open 2 ,
 .Xr daemon 3 ,
@@ -251,7 +296,7 @@ library function.
 The
 .Nm pidfile
 functionality is based on ideas from
-.An John-Mark Gurney Aq jmg at FreeBSD.org .
+.An John-Mark Gurney Aq Mt jmg at FreeBSD.org .
 .Pp
 The code and manual page was written by
-.An Pawel Jakub Dawidek Aq pjd at FreeBSD.org .
+.An Pawel Jakub Dawidek Aq Mt pjd at FreeBSD.org .
diff --git a/src/libbsd.map b/src/libbsd.map
index 47d7df5..4a41586 100644
--- a/src/libbsd.map
+++ b/src/libbsd.map
@@ -142,4 +142,6 @@ LIBBSD_0.8 {
 
 LIBBSD_0.9 {
     flopenat;
+
+    pidfile_fileno;
 } LIBBSD_0.8;
diff --git a/src/pidfile.c b/src/pidfile.c
index b958cc1..6aedef2 100644
--- a/src/pidfile.c
+++ b/src/pidfile.c
@@ -29,20 +29,27 @@
 #include <sys/file.h>
 #include <sys/stat.h>
 
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <string.h>
 #include <time.h>
-#include <err.h>
-#include <errno.h>
-#include <libutil.h>
+#include <unistd.h>
+
+struct pidfh {
+	int	pf_fd;
+	char	*pf_path;
+	dev_t	pf_dev;
+	ino_t	pf_ino;
+};
 
 static int _pidfile_remove(struct pidfh *pfh, int freeit);
 
 static int
-pidfile_verify(struct pidfh *pfh)
+pidfile_verify(const struct pidfh *pfh)
 {
 	struct stat sb;
 
@@ -114,25 +121,33 @@ pidfile_open(const char *path, mode_t mode, pid_t *pidptr)
 	fd = flopen(pfh->pf_path,
 	    O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode);
 	if (fd == -1) {
-		count = 0;
-		rqtp.tv_sec = 0;
-		rqtp.tv_nsec = 5000000;
-		if (errno == EWOULDBLOCK && pidptr != NULL) {
-		again:
-			errno = pidfile_read(pfh->pf_path, pidptr);
-			if (errno == 0)
+		if (errno == EWOULDBLOCK) {
+			if (pidptr == NULL) {
 				errno = EEXIST;
-			else if (errno == EAGAIN) {
-				if (++count <= 3) {
+			} else {
+				count = 20;
+				rqtp.tv_sec = 0;
+				rqtp.tv_nsec = 5000000;
+				for (;;) {
+					errno = pidfile_read(pfh->pf_path,
+					                     pidptr);
+					if (errno != EAGAIN || --count == 0)
+						break;
 					nanosleep(&rqtp, 0);
-					goto again;
 				}
+				if (errno == EAGAIN)
+					*pidptr = -1;
+				if (errno == 0 || errno == EAGAIN)
+					errno = EEXIST;
 			}
 		}
+		error = errno;
 		free(pfh->pf_path);
 		free(pfh);
+		errno = error;
 		return (NULL);
 	}
+
 	/*
 	 * Remember file information, so in pidfile_write() we are sure we write
 	 * to the proper descriptor.
@@ -251,3 +266,14 @@ pidfile_remove(struct pidfh *pfh)
 
 	return (_pidfile_remove(pfh, 1));
 }
+
+int
+pidfile_fileno(const struct pidfh *pfh)
+{
+
+	if (pfh == NULL || pfh->pf_fd == -1) {
+		errno = EINVAL;
+		return (-1);
+	}
+	return (pfh->pf_fd);
+}
commit 3d88c999b4a0ac2820c84732262ba7a0ad44e421
Author: Guillem Jover <guillem at hadrons.org>
Date:   Tue May 15 00:41:26 2018 +0200

    Update arc4random() headers from OpenBSD
    
    Split Linux support into its own header separate from the generic Unix
    to fix a Linux-specific issue with clone(). Reset rsp to NULL on failure.

diff --git a/COPYING b/COPYING
index 021e67e..b768093 100644
--- a/COPYING
+++ b/COPYING
@@ -429,6 +429,7 @@ Files:
  man/strlcpy.3bsd
  man/strtonum.3bsd
  src/arc4random.c
+ src/arc4random_linux.h
  src/arc4random_openbsd.h
  src/arc4random_uniform.c
  src/arc4random_unix.h
diff --git a/src/Makefile.am b/src/Makefile.am
index ad83dbf..d72802e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,9 +60,10 @@ libbsd_la_LDFLAGS = \
 libbsd_la_SOURCES = \
 	arc4random.c \
 	arc4random.h \
-	arc4random_unix.h \
+	arc4random_linux.h \
 	arc4random_openbsd.h \
 	arc4random_uniform.c \
+	arc4random_unix.h \
 	bsd_getopt.c \
 	chacha_private.h \
 	closefrom.c \
diff --git a/src/arc4random.h b/src/arc4random.h
index 073f90e..803ef86 100644
--- a/src/arc4random.h
+++ b/src/arc4random.h
@@ -34,6 +34,8 @@ getentropy(void *buf, size_t len);
 
 #if defined(__OpenBSD__)
 #include "arc4random_openbsd.h"
+#elif defined(__linux__)
+#include "arc4random_linux.h"
 #else
 #include "arc4random_unix.h"
 #endif
diff --git a/src/arc4random_linux.h b/src/arc4random_linux.h
new file mode 100644
index 0000000..7a2ca1e
--- /dev/null
+++ b/src/arc4random_linux.h
@@ -0,0 +1,88 @@
+/*	$OpenBSD: arc4random_linux.h,v 1.11 2016/06/30 12:19:51 bcook Exp $	*/
+
+/*
+ * Copyright (c) 1996, David Mazieres <dm at uun.org>
+ * Copyright (c) 2008, Damien Miller <djm at openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus at openbsd.org>
+ * Copyright (c) 2014, Theo de Raadt <deraadt at openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Stub functions for portability.
+ */
+
+#include <sys/mman.h>
+
+#include <pthread.h>
+#include <signal.h>
+
+static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
+#define _ARC4_LOCK()   pthread_mutex_lock(&arc4random_mtx)
+#define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx)
+
+#ifdef __GLIBC__
+extern void *__dso_handle;
+extern int __register_atfork(void (*)(void), void(*)(void), void (*)(void), void *);
+#define _ARC4_ATFORK(f) __register_atfork(NULL, NULL, (f), __dso_handle)
+#else
+#define _ARC4_ATFORK(f) pthread_atfork(NULL, NULL, (f))
+#endif
+
+static inline void
+_getentropy_fail(void)
+{
+	raise(SIGKILL);
+}
+
+static volatile sig_atomic_t _rs_forked;
+
+static inline void
+_rs_forkhandler(void)
+{
+	_rs_forked = 1;
+}
+
+static inline void
+_rs_forkdetect(void)
+{
+	static pid_t _rs_pid = 0;
+	pid_t pid = getpid();
+
+	/* XXX unusual calls to clone() can bypass checks */
+	if (_rs_pid == 0 || _rs_pid == 1 || _rs_pid != pid || _rs_forked) {
+		_rs_pid = pid;
+		_rs_forked = 0;
+		if (rs)
+			memset(rs, 0, sizeof(*rs));
+	}
+}
+
+static inline int
+_rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
+{
+	if ((*rsp = mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE,
+	    MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
+		return (-1);
+
+	if ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE,
+	    MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
+		munmap(*rsp, sizeof(**rsp));
+		*rsp = NULL;
+		return (-1);
+	}
+
+	_ARC4_ATFORK(_rs_forkhandler);
+	return (0);
+}
diff --git a/src/arc4random_unix.h b/src/arc4random_unix.h
index 73b0cd5..0e37683 100644
--- a/src/arc4random_unix.h
+++ b/src/arc4random_unix.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: arc4random_linux.h,v 1.8 2014/08/13 06:04:10 deraadt Exp $	*/
+/*	$OpenBSD: arc4random_freebsd.h,v 1.4 2016/06/30 12:19:51 bcook Exp $	*/
 
 /*
  * Copyright (c) 1996, David Mazieres <dm at uun.org>
@@ -84,6 +84,7 @@ _rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
 	if ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE,
 	    MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
 		munmap(*rsp, sizeof(**rsp));
+		*rsp = NULL;
 		return (-1);
 	}
 
commit e42381dc51065e6d48ae982a01888dddde26ef7b
Author: Guillem Jover <guillem at hadrons.org>
Date:   Tue May 15 00:41:26 2018 +0200

    Update getentropy() code from OpenBSD
    
    Includes changes to handle the Linux syscall blocking when there is not
    enough entropy during boot, by switching it to non-blocking mode and
    falling back to the alternative implementations. Man page URL reference
    fixes. Build fixes for Mac OS X.
    
    Fixes: https://bugs.debian.org/898088

diff --git a/src/getentropy_aix.c b/src/getentropy_aix.c
index d4ccab7..d759fe0 100644
--- a/src/getentropy_aix.c
+++ b/src/getentropy_aix.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: getentropy_aix.c,v 1.3 2015/08/25 17:26:43 deraadt Exp $	*/
+/*	$OpenBSD: getentropy_aix.c,v 1.5 2016/08/07 03:27:21 tb Exp $	*/
 
 /*
  * Copyright (c) 2015 Michael Felt <aixtools at gmail.com>
@@ -18,7 +18,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  * Emulation of getentropy(2) as documented at:
- * http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2
+ * http://man.openbsd.org/getentropy.2
  */
 /*
  * -lperfstat is needed for the psuedo entropy data
diff --git a/src/getentropy_bsd.c b/src/getentropy_bsd.c
index d45901b..705f65b 100644
--- a/src/getentropy_bsd.c
+++ b/src/getentropy_bsd.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: getentropy_freebsd.c,v 1.1 2014/11/03 06:23:30 bcook Exp $	*/
+/*	$OpenBSD: getentropy_freebsd.c,v 1.3 2016/08/07 03:27:21 tb Exp $	*/
 
 /*
  * Copyright (c) 2014 Pawel Jakub Dawidek <pjd at FreeBSD.org>
@@ -17,7 +17,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  * Emulation of getentropy(2) as documented at:
- * http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2
+ * http://man.openbsd.org/getentropy.2
  */
 
 #include <sys/types.h>
diff --git a/src/getentropy_hpux.c b/src/getentropy_hpux.c
index 294d83a..5be096a 100644
--- a/src/getentropy_hpux.c
+++ b/src/getentropy_hpux.c
@@ -17,7 +17,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  * Emulation of getentropy(2) as documented at:
- * http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2
+ * http://man.openbsd.org/getentropy.2
  */
 
 #include <sys/types.h>
diff --git a/src/getentropy_hurd.c b/src/getentropy_hurd.c
index 194d9c5..738dc3b 100644
--- a/src/getentropy_hurd.c
+++ b/src/getentropy_hurd.c
@@ -17,7 +17,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  * Emulation of getentropy(2) as documented at:
- * http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2
+ * http://man.openbsd.org/getentropy.2
  */
 
 #define	_POSIX_C_SOURCE	199309L
@@ -110,7 +110,7 @@ getentropy(void *buf, size_t len)
 	 *     - Do the best under the circumstances....
 	 *
 	 * This code path exists to bring light to the issue that Hurd
-	 * does not provide a failsafe API for entropy collection.
+	 * still does not provide a failsafe API for entropy collection.
 	 *
 	 * We hope this demonstrates that Hurd should either get a
 	 * sysctl ABI, or consider providing a new failsafe API which
diff --git a/src/getentropy_linux.c b/src/getentropy_linux.c
index d7a8ae5..74d965e 100644
--- a/src/getentropy_linux.c
+++ b/src/getentropy_linux.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: getentropy_linux.c,v 1.40 2015/08/25 17:26:43 deraadt Exp $	*/
+/*	$OpenBSD: getentropy_linux.c,v 1.45 2018/03/13 22:53:28 bcook Exp $	*/
 
 /*
  * Copyright (c) 2014 Theo de Raadt <deraadt at openbsd.org>
@@ -17,7 +17,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  * Emulation of getentropy(2) as documented at:
- * http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2
+ * http://man.openbsd.org/getentropy.2
  */
 
 #define	_POSIX_C_SOURCE	199309L
@@ -75,7 +75,7 @@
 int	getentropy(void *buf, size_t len);
 
 static int gotdata(char *buf, size_t len);
-#ifdef SYS_getrandom
+#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
 static int getentropy_getrandom(void *buf, size_t len);
 #endif
 static int getentropy_urandom(void *buf, size_t len);
@@ -95,15 +95,18 @@ getentropy(void *buf, size_t len)
 		return (-1);
 	}
 
-#ifdef SYS_getrandom
+#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
 	/*
-	 * Try descriptor-less getrandom()
+	 * Try descriptor-less getrandom(), in non-blocking mode.
+	 *
+	 * The design of Linux getrandom is broken.  It has an
+	 * uninitialized phase coupled with blocking behaviour, which
+	 * is unacceptable from within a library at boot time without
+	 * possible recovery. See http://bugs.python.org/issue26839#msg267745
 	 */
 	ret = getentropy_getrandom(buf, len);
 	if (ret != -1)
 		return (ret);
-	if (errno != ENOSYS)
-		return (-1);
 #endif
 
 	/*
@@ -121,7 +124,7 @@ getentropy(void *buf, size_t len)
 	 * Try to use sysctl CTL_KERN, KERN_RANDOM, RANDOM_UUID.
 	 * sysctl is a failsafe API, so it guarantees a result.  This
 	 * should work inside a chroot, or when file descriptors are
-	 * exhuasted.
+	 * exhausted.
 	 *
 	 * However this can fail if the Linux kernel removes support
 	 * for sysctl.  Starting in 2007, there have been efforts to
@@ -157,7 +160,7 @@ getentropy(void *buf, size_t len)
 	 *     - Do the best under the circumstances....
 	 *
 	 * This code path exists to bring light to the issue that Linux
-	 * does not provide a failsafe API for entropy collection.
+	 * still does not provide a failsafe API for entropy collection.
 	 *
 	 * We hope this demonstrates that Linux should either retain their
 	 * sysctl ABI, or consider providing a new failsafe API which
@@ -191,7 +194,7 @@ gotdata(char *buf, size_t len)
 	return (0);
 }
 
-#ifdef SYS_getrandom
+#if defined(SYS_getrandom) && defined(GRND_NONBLOCK)
 static int
 getentropy_getrandom(void *buf, size_t len)
 {
@@ -200,7 +203,7 @@ getentropy_getrandom(void *buf, size_t len)
 	if (len > 256)
 		return (-1);
 	do {
-		ret = syscall(SYS_getrandom, buf, len, 0);
+		ret = syscall(SYS_getrandom, buf, len, GRND_NONBLOCK);
 	} while (ret == -1 && errno == EINTR);
 
 	if (ret != (int)len)
diff --git a/src/getentropy_osx.c b/src/getentropy_osx.c
index db67c4b..bcdbce5 100644
--- a/src/getentropy_osx.c
+++ b/src/getentropy_osx.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: getentropy_osx.c,v 1.8 2014/07/21 20:19:47 guenther Exp $	*/
+/*	$OpenBSD: getentropy_osx.c,v 1.11 2016/09/03 15:24:09 bcook Exp $	*/
 
 /*
  * Copyright (c) 2014 Theo de Raadt <deraadt at openbsd.org>
@@ -17,9 +17,10 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  * Emulation of getentropy(2) as documented at:
- * http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2
+ * http://man.openbsd.org/getentropy.2
  */
 
+#include <TargetConditionals.h>
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/ioctl.h>
@@ -45,14 +46,18 @@
 #include <mach/mach_time.h>
 #include <mach/mach_host.h>
 #include <mach/host_info.h>
+#if TARGET_OS_OSX
 #include <sys/socketvar.h>
 #include <sys/vmmeter.h>
+#endif
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#if TARGET_OS_OSX
 #include <netinet/udp.h>
 #include <netinet/ip_var.h>
 #include <netinet/tcp_var.h>
 #include <netinet/udp_var.h>
+#endif
 #include <CommonCrypto/CommonDigest.h>
 #define SHA512_Update(a, b, c)	(CC_SHA512_Update((a), (b), (c)))
 #define SHA512_Init(xxx) (CC_SHA512_Init((xxx)))
@@ -207,9 +212,11 @@ nodevrandom:
 	return (-1);
 }
 
+#if TARGET_OS_OSX
 static int tcpmib[] = { CTL_NET, AF_INET, IPPROTO_TCP, TCPCTL_STATS };
 static int udpmib[] = { CTL_NET, AF_INET, IPPROTO_UDP, UDPCTL_STATS };
 static int ipmib[] = { CTL_NET, AF_INET, IPPROTO_IP, IPCTL_STATS };
+#endif
 static int kmib[] = { CTL_KERN, KERN_USRSTACK };
 static int hwmib[] = { CTL_HW, HW_USERMEM };
 
@@ -229,9 +236,11 @@ getentropy_fallback(void *buf, size_t len)
 	pid_t pid;
 	size_t i, ii, m;
 	char *p;
+#if TARGET_OS_OSX
 	struct tcpstat tcpstat;
 	struct udpstat udpstat;
 	struct ipstat ipstat;
+#endif
 	uint64_t mach_time;
 	unsigned int idata;
 	void *addr;
@@ -266,6 +275,7 @@ getentropy_fallback(void *buf, size_t len)
 			HX(sysctl(hwmib, sizeof(hwmib) / sizeof(hwmib[0]),
 			    &idata, &ii, NULL, 0) == -1, idata);
 
+#if TARGET_OS_OSX
 			ii = sizeof(tcpstat);
 			HX(sysctl(tcpmib, sizeof(tcpmib) / sizeof(tcpmib[0]),
 			    &tcpstat, &ii, NULL, 0) == -1, tcpstat);
@@ -277,6 +287,7 @@ getentropy_fallback(void *buf, size_t len)
 			ii = sizeof(ipstat);
 			HX(sysctl(ipmib, sizeof(ipmib) / sizeof(ipmib[0]),
 			    &ipstat, &ii, NULL, 0) == -1, ipstat);
+#endif
 
 			HX((pid = getpid()) == -1, pid);
 			HX((pid = getsid(pid)) == -1, pid);
diff --git a/src/getentropy_solaris.c b/src/getentropy_solaris.c
index ca787f8..f0fcdcf 100644
--- a/src/getentropy_solaris.c
+++ b/src/getentropy_solaris.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: getentropy_solaris.c,v 1.10 2015/08/25 17:26:43 deraadt Exp $	*/
+/*	$OpenBSD: getentropy_solaris.c,v 1.12 2016/08/07 03:27:21 tb Exp $	*/
 
 /*
  * Copyright (c) 2014 Theo de Raadt <deraadt at openbsd.org>
@@ -17,7 +17,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  * Emulation of getentropy(2) as documented at:
- * http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2
+ * http://man.openbsd.org/getentropy.2
  */
 
 #include <sys/types.h>
commit 993828d84eed0468c6c15b2818e534e6b134b8e4
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 01:11:46 2018 +0200

    Add flopenat() function from FreeBSD

diff --git a/COPYING b/COPYING
index 9ffc6b4..021e67e 100644
--- a/COPYING
+++ b/COPYING
@@ -367,7 +367,7 @@ License: BSD-2-clause
 Files:
  src/flopen.c
 Copyright:
- Copyright © 2007 Dag-Erling Coïdan Smørgrav
+ Copyright © 2007-2009 Dag-Erling Coïdan Smørgrav
  All rights reserved.
 License: BSD-2-clause-verbatim
  Redistribution and use in source and binary forms, with or without
diff --git a/include/bsd/libutil.h b/include/bsd/libutil.h
index e5f148a..2c4c731 100644
--- a/include/bsd/libutil.h
+++ b/include/bsd/libutil.h
@@ -62,6 +62,7 @@ int humanize_number(char *buf, size_t len, int64_t bytes,
 int expand_number(const char *_buf, uint64_t *_num);
 
 int flopen(const char *_path, int _flags, ...);
+int flopenat(int dirfd, const char *path, int flags, ...);
 
 struct pidfh *pidfile_open(const char *path, mode_t mode, pid_t *pidptr);
 int pidfile_write(struct pidfh *pfh);
diff --git a/man/flopen.3bsd b/man/flopen.3bsd
index ab58b28..b3cd8c9 100644
--- a/man/flopen.3bsd
+++ b/man/flopen.3bsd
@@ -25,12 +25,13 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 6, 2009
+.Dd July 28, 2017
 .Dt FLOPEN 3bsd
 .Os
 .Sh NAME
-.Nm flopen
-.Nd reliably open and lock a file
+.Nm flopen ,
+.Nm flopenat
+.Nd "Reliably open and lock a file"
 .Sh LIBRARY
 .ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
 .Lb libbsd
@@ -44,6 +45,10 @@ for include usage.)
 .Fn flopen "const char *path" "int flags"
 .Ft int
 .Fn flopen "const char *path" "int flags" "mode_t mode"
+.Ft int
+.Fn flopenat "int fd" "const char *path" "int flags"
+.Ft int
+.Fn flopenat "int fd" "const char *path" "int flags" "mode_t mode"
 .Sh DESCRIPTION
 The
 .Fn flopen
@@ -53,7 +58,7 @@ It is essentially equivalent with calling
 with the same parameters followed by
 .Fn flock
 with an
-.Va operation
+.Fa operation
 argument of
 .Dv LOCK_EX ,
 except that
@@ -65,7 +70,7 @@ files, mailboxes and other kinds of files which are used for
 synchronization between processes.
 .Pp
 If
-.Va flags
+.Fa flags
 includes
 .Dv O_NONBLOCK
 and the file is already locked,
@@ -78,11 +83,32 @@ to
 As with
 .Fn open ,
 the additional
-.Va mode
+.Fa mode
 argument is required if
-.Va flags
+.Fa flags
 includes
 .Dv O_CREAT .
+.Pp
+The
+.Fn flopenat
+function is equivalent to the
+.Fn flopen
+function except in the case where the
+.Fa path
+specifies a relative path.
+In this case the file to be opened is determined relative to the directory
+associated with the file descriptor
+.Fa fd
+instead of the current working directory.
+If
+.Fn flopenat
+is passed the special value
+.Dv AT_FDCWD
+in the
+.Fa fd
+parameter, the current working directory is used
+and the behavior is identical to a call to
+.Fn flopen .
 .Sh RETURN VALUES
 If successful,
 .Fn flopen
@@ -102,4 +128,4 @@ and
 The
 .Nm
 function and this manual page were written by
-.An Dag-Erling Sm\(/orgrav Aq des at FreeBSD.org .
+.An Dag-Erling Sm\(/orgrav Aq Mt des at FreeBSD.org .
diff --git a/src/flopen.c b/src/flopen.c
index aa506f5..b9972c9 100644
--- a/src/flopen.c
+++ b/src/flopen.c
@@ -1,5 +1,7 @@
 /*-
- * Copyright (c) 2007 Dag-Erling Coïdan Smørgrav
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2007-2009 Dag-Erling Coïdan Smørgrav
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,13 +32,21 @@
 #include <sys/stat.h>
 
 #include <errno.h>
-#include <fcntl.h>
 #include <stdarg.h>
 #include <unistd.h>
+
 #include <libutil.h>
 
-int
-flopen(const char *path, int flags, ...)
+/*
+ * Reliably open and lock a file.
+ *
+ * Please do not modify this code without first reading the revision history
+ * and discussing your changes with <des at freebsd.org>.  Don't be fooled by the
+ * code's apparent simplicity; there would be no need for this function if it
+ * was easy to get right.
+ */
+static int
+vflopenat(int dirfd, const char *path, int flags, va_list ap)
 {
 	int fd, operation, serrno, trunc;
 	struct stat sb, fsb;
@@ -48,11 +58,7 @@ flopen(const char *path, int flags, ...)
 
 	mode = 0;
 	if (flags & O_CREAT) {
-		va_list ap;
-
-		va_start(ap, flags);
 		mode = (mode_t)va_arg(ap, int); /* mode_t promoted to int */
-		va_end(ap);
 	}
 
         operation = LOCK_EX;
@@ -63,7 +69,7 @@ flopen(const char *path, int flags, ...)
 	flags &= ~O_TRUNC;
 
 	for (;;) {
-		if ((fd = open(path, flags, mode)) == -1)
+		if ((fd = openat(dirfd, path, flags, mode)) == -1)
 			/* non-existent or no access */
 			return (-1);
 		if (flock(fd, operation) == -1) {
@@ -73,7 +79,7 @@ flopen(const char *path, int flags, ...)
 			errno = serrno;
 			return (-1);
 		}
-		if (stat(path, &sb) == -1) {
+		if (fstatat(dirfd, path, &sb, 0) == -1) {
 			/* disappeared from under our feet */
 			(void)close(fd);
 			continue;
@@ -98,6 +104,42 @@ flopen(const char *path, int flags, ...)
 			errno = serrno;
 			return (-1);
 		}
+		/*
+		 * The following change is provided as a specific example to
+		 * avoid.
+		 */
+#if 0
+		if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
+			serrno = errno;
+			(void)close(fd);
+			errno = serrno;
+			return (-1);
+		}
+#endif
 		return (fd);
 	}
 }
+
+int
+flopen(const char *path, int flags, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, flags);
+	ret = vflopenat(AT_FDCWD, path, flags, ap);
+	va_end(ap);
+	return (ret);
+}
+
+int
+flopenat(int dirfd, const char *path, int flags, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, flags);
+	ret = vflopenat(dirfd, path, flags, ap);
+	va_end(ap);
+	return (ret);
+}
diff --git a/src/libbsd.map b/src/libbsd.map
index 304c593..47d7df5 100644
--- a/src/libbsd.map
+++ b/src/libbsd.map
@@ -139,3 +139,7 @@ LIBBSD_0.7 {
 LIBBSD_0.8 {
     explicit_bzero;
 } LIBBSD_0.7;
+
+LIBBSD_0.9 {
+    flopenat;
+} LIBBSD_0.8;
commit 30b4d507540bada5533eea60a850090ccc54d397
Author: Guillem Jover <guillem at hadrons.org>
Date:   Mon May 21 03:00:47 2018 +0200

    Add __arraycount() macro from NetBSD

diff --git a/include/bsd/sys/cdefs.h b/include/bsd/sys/cdefs.h
index d1cc419..98333d1 100644
--- a/include/bsd/sys/cdefs.h
+++ b/include/bsd/sys/cdefs.h
@@ -156,6 +156,12 @@
 #endif
 
 /*
+ * Return the number of elements in a statically-allocated array,
+ * __x.
+ */
+#define	__arraycount(__x)	(sizeof(__x) / sizeof(__x[0]))
+
+/*
  * We define this here since <stddef.h>, <sys/queue.h>, and <sys/types.h>
  * require it.
  */


More information about the libbsd mailing list