address the strnvis(3) portability fiasco

strnvis originates on OpenBSD.  When NetBSD added it to their libc
they decided to swap the argument.  Without starting a holy war on
the "best" argument order, adding an implementation of a function
that's widely available and making its signature purposefully
incompatible is beyond justification.  FreeBSD (and so macos too?)
followed NetBSD in this, so we end up with *two* major and incompatible
strnvis implementations.  libbsd is in a limbo, they started with
the OpenBSD version but they'll probably switch to the NetBSD version
in the future.

That's why we can't have nice things.

Do the right thing(tm) and check for the presence of the original
strnvis(3), if not available or broken use the bundled one.
This commit is contained in:
Omar Polo 2023-08-07 15:39:57 +00:00
parent ddbcd3c13f
commit d13b044d59
6 changed files with 407 additions and 2 deletions

View File

@ -19,7 +19,8 @@ DISTFILES = Makefile \
strlcpy.c \
strtonum.c \
tree.h \
vasprintf.c
vasprintf.c \
vis.c
all:
false
@ -27,6 +28,8 @@ all:
dist: ${DISTFILES}
mkdir -p ${DESTDIR}/
${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/
mkdir -p ${DESTDIR}/vis
${INSTALL} -m 0644 vis/vis.h ${DESTDIR}/vis
.PHONY: all dist
include ../config.mk

272
compat/vis.c Normal file
View File

@ -0,0 +1,272 @@
/* $OpenBSD: vis.c,v 1.26 2022/05/04 18:57:50 deraadt Exp $ */
/*-
* Copyright (c) 1989, 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.
*/
#include <sys/types.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <vis.h>
static int
isoctal(int c)
{
u_char uc = c;
return uc >= '0' && uc <= '7';
}
static int
isvisible(int c, int flag)
{
int vis_sp = flag & VIS_SP;
int vis_tab = flag & VIS_TAB;
int vis_nl = flag & VIS_NL;
int vis_safe = flag & VIS_SAFE;
int vis_glob = flag & VIS_GLOB;
int vis_all = flag & VIS_ALL;
u_char uc = c;
if (c == '\\' || !vis_all) {
if ((u_int)c <= UCHAR_MAX && isascii(uc) &&
((c != '*' && c != '?' && c != '[' && c != '#') || !vis_glob) &&
isgraph(uc))
return 1;
if (!vis_sp && c == ' ')
return 1;
if (!vis_tab && c == '\t')
return 1;
if (!vis_nl && c == '\n')
return 1;
if (vis_safe && (c == '\b' || c == '\007' || c == '\r' || isgraph(uc)))
return 1;
}
return 0;
}
/*
* vis - visually encode characters
*/
char *
vis(char *dst, int c, int flag, int nextc)
{
int vis_dq = flag & VIS_DQ;
int vis_noslash = flag & VIS_NOSLASH;
int vis_cstyle = flag & VIS_CSTYLE;
int vis_octal = flag & VIS_OCTAL;
int vis_glob = flag & VIS_GLOB;
if (isvisible(c, flag)) {
if ((c == '"' && vis_dq) ||
(c == '\\' && !vis_noslash))
*dst++ = '\\';
*dst++ = c;
*dst = '\0';
return (dst);
}
if (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;
}
}
if (((c & 0177) == ' ') || vis_octal ||
(vis_glob && (c == '*' || c == '?' || c == '[' || c == '#'))) {
*dst++ = '\\';
*dst++ = ((u_char)c >> 6 & 07) + '0';
*dst++ = ((u_char)c >> 3 & 07) + '0';
*dst++ = ((u_char)c & 07) + '0';
goto done;
}
if (!vis_noslash)
*dst++ = '\\';
if (c & 0200) {
c &= 0177;
*dst++ = 'M';
}
if (iscntrl((u_char)c)) {
*dst++ = '^';
if (c == 0177)
*dst++ = '?';
else
*dst++ = c + '@';
} else {
*dst++ = '-';
*dst++ = c;
}
done:
*dst = '\0';
return (dst);
}
/*
* strvis, strnvis, 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.
*
* 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)
{
char c;
char *start;
for (start = dst; (c = *src);)
dst = vis(dst, c, flag, *++src);
*dst = '\0';
return (dst - start);
}
int
strnvis(char *dst, const char *src, size_t siz, int flag)
{
int vis_dq = flag & VIS_DQ;
int vis_noslash = flag & VIS_NOSLASH;
char *start, *end;
char tbuf[5];
int c, i;
i = 0;
for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
if (isvisible(c, flag)) {
if ((c == '"' && vis_dq) ||
(c == '\\' && !vis_noslash)) {
/* need space for the extra '\\' */
if (dst + 1 >= end) {
i = 2;
break;
}
*dst++ = '\\';
}
i = 1;
*dst++ = c;
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 **outp, const char *src, int flag)
{
char *buf;
int len, serrno;
buf = reallocarray(NULL, 4, strlen(src) + 1);
if (buf == NULL)
return -1;
len = strvis(buf, src, flag);
serrno = errno;
*outp = realloc(buf, len + 1);
if (*outp == NULL) {
*outp = buf;
errno = serrno;
}
return (len);
}
int
strvisx(char *dst, const char *src, size_t len, int flag)
{
char c;
char *start;
for (start = dst; len > 1; len--) {
c = *src;
dst = vis(dst, c, flag, *++src);
}
if (len)
dst = vis(dst, *src, flag, '\0');
*dst = '\0';
return (dst - start);
}

90
compat/vis/vis.h Normal file
View File

@ -0,0 +1,90 @@
/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */
/* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */
/*-
* Copyright (c) 1990 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.
*
* @(#)vis.h 5.9 (Berkeley) 4/3/91
*/
#ifndef _VIS_H_
#define _VIS_H_
/*
* to select alternate encoding format
*/
#define VIS_OCTAL 0x01 /* use octal \ddd format */
#define VIS_CSTYLE 0x02 /* 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_WHITE (VIS_SP | VIS_TAB | VIS_NL)
#define VIS_SAFE 0x20 /* only encode "unsafe" characters */
#define VIS_DQ 0x200 /* backslash-escape double quotes */
#define VIS_ALL 0x400 /* encode all characters */
/*
* other
*/
#define VIS_NOSLASH 0x40 /* inhibit printing '\' */
#define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */
/*
* unvis return codes
*/
#define UNVIS_VALID 1 /* character valid */
#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */
#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */
#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */
#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */
/*
* unvis flags
*/
#define UNVIS_END 1 /* no more characters */
#include <sys/cdefs.h>
char *vis(char *, int, int, int);
int strvis(char *, const char *, int);
int stravis(char **, const char *, int);
int strnvis(char *, const char *, size_t, int)
__attribute__ ((__bounded__(__string__,1,3)));
int strvisx(char *, const char *, size_t, int)
__attribute__ ((__bounded__(__string__,1,3)));
int strunvis(char *, const char *);
int unvis(char *, char, int *, int);
ssize_t strnunvis(char *, const char *, size_t)
__attribute__ ((__bounded__(__string__,1,3)));
#endif /* !_VIS_H_ */

13
configure vendored
View File

@ -120,6 +120,7 @@ echo "file config.log: writing..."
NEED_GNU_SOURCE=0
NEED_OPENBSD_SOURCE=0
NEED_LIBBSD_OPENBSD_VIS=0
COMPATS=
COMP="${CC} ${CFLAGS} -Wno-unused -Werror"
@ -192,6 +193,10 @@ runtest() {
NEED_OPENBSD_SOURCE=1
return 0
fi
if [ "$4" = -DLIBBSD_OPENBSD_VIS ]; then
NEED_LIBBSD_OPENBSD_VIS=1
return 0
fi
if [ -n "$3" ]; then
CFLAGS="$CFLAGS $3"
fi
@ -284,6 +289,7 @@ runtest strlcpy STRLCPY || true
runtest strtonum STRTONUM -D_OPENBSD_SOURCE || true
runtest tree_h TREE_H || true
runtest vasprintf VASPRINTF -D_GNU_SOURCE || true
runtest vis VIS -DLIBBSD_OPENBSD_VIS || true
deptest libevent2 LIBEVENT2 || true
@ -314,6 +320,10 @@ if [ ${HAVE_QUEUE_H} -eq 0 -o ${HAVE_IMSG} -eq 0 -o ${HAVE_TREE_H} -eq 0 ]; then
CFLAGS="${CFLAGS} -I ${PWD}/compat"
fi
if [ ${HAVE_VIS} -eq 0 ]; then
CFLAGS="${CFLAGS} -I ${PWD}/compat/vis"
fi
if [ $HAVE_LIBEVENT2 -eq 1 ]; then
CFLAGS="$CFLAGS -DHAVE_LIBEVENT2=1"
fi
@ -324,6 +334,9 @@ fi
if [ $NEED_OPENBSD_SOURCE = 1 ]; then
CFLAGS="$CFLAGS -D_OPENBSD_SOURCE"
fi
if [ $NEED_LIBBSD_OPENBSD_VIS = 1 ]; then
CFLAGS="$CFLAGS -DLIBBSD_OPENBSD_VIS"
fi
CFLAGS="${CFLAGS} ${CDIAGFLAGS}"

View File

@ -24,7 +24,8 @@ DISTFILES = Makefile \
strlcpy.c \
strtonum.c \
tree_h.c \
vasprintf.c
vasprintf.c \
vis.c
all:
false

26
have/vis.c Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.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.
*/
#include <stdlib.h>
#include <vis.h>
int
main(void)
{
char buf[128];
return strnvis(buf, "Hello, world!\n", sizeof(buf), 0);
}