commit d13b044d59619a52277c229eb37d38265690fb64 from: Omar Polo date: Mon Aug 07 15:39:57 2023 UTC 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. commit - ddbcd3c13f2159113bb7e9921a1bec13755c5d43 commit + d13b044d59619a52277c229eb37d38265690fb64 blob - f820f71c219debfb93731a6abbb6ea5fb284f4fc blob + 65ec3ef2b2dce2eda370c789d62e7c02b6eadb74 --- compat/Makefile +++ compat/Makefile @@ -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 blob - /dev/null blob + 59b7d6b81777229668dfbb0bb1aaaca8e126eb8c (mode 644) --- /dev/null +++ compat/vis/vis.h @@ -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 + +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_ */ blob - /dev/null blob + 9ebdc7207f5acf508340c7b3737f3cf990c49b84 (mode 644) --- /dev/null +++ compat/vis.c @@ -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 +#include +#include +#include +#include +#include +#include + +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); +} blob - 83332f998b3adf98066ee340ece5f5cb0088feb5 blob + 7ae1d5a7a1b485c7e396bb215c903e7b84c33c87 --- configure +++ configure @@ -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" @@ -190,6 +191,10 @@ runtest() { fi if [ "$4" = -D_OPENBSD_SOURCE ]; then 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 @@ -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 ${ 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}" blob - ee214d5a44e1ad365a8d4c155fe1188d567e452a blob + 9fd19e83b7c9097930bede923b3dfbdacafd9e04 --- have/Makefile +++ have/Makefile @@ -24,7 +24,8 @@ DISTFILES = Makefile \ strlcpy.c \ strtonum.c \ tree_h.c \ - vasprintf.c + vasprintf.c \ + vis.c all: false blob - /dev/null blob + 85a4307d23c00e101a1331ba094cfa8aaa167748 (mode 644) --- /dev/null +++ have/vis.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Omar Polo + * + * 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 +#include + +int +main(void) +{ + char buf[128]; + + return strnvis(buf, "Hello, world!\n", sizeof(buf), 0); +}