Update vis/unvis modules from NetBSD

This commit is contained in:
Guillem Jover
2018-05-21 03:09:05 +02:00
parent 3efad64155
commit 2d7de186e9
7 changed files with 1607 additions and 538 deletions

View File

@@ -144,4 +144,16 @@ LIBBSD_0.9 {
flopenat;
pidfile_fileno;
nvis;
snvis;
stravis;
strenvisx;
strnunvisx;
strsenvisx;
strsnvis;
strsnvisx;
strsvis;
strsvisx;
svis;
} LIBBSD_0.8;

View File

@@ -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);
}
ssize_t
strnunvis(char *dst, const char *src, size_t sz)
{
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 (int)(dst - start);
}
int
strunvisx(char *dst, const char *src, int flag)
{
char c;
char *start = dst;
int state = 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);
return strnunvisx(dst, (size_t)~0, src, flag);
}
int
strunvis(char *dst, const char *src)
{
return strnunvisx(dst, (size_t)~0, src, 0);
}
int
strnunvis(char *dst, size_t dlen, const char *src)
{
return strnunvisx(dst, dlen, src, 0);
}

876
src/vis.c
View File

@@ -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.
*/
#include <sys/types.h>
#include <limits.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <vis.h>
/*-
* 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.
*/
#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)))))
#include <sys/cdefs.h>
#include <sys/types.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 <limits.h>
#include <stdio.h>
#include <string.h>
#define _DIAGASSERT(x)
/*
* 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.
*/
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;
}
/*
* 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;
return dst;
}
/* 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;
}
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;
}
/* 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;
}
}
/* 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;
}
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);
}
/*
* 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 *dst, int c, int flag, int nextc)
vis(char *mbdst, int c, int flags, int nextc)
{
c = (unsigned char)c;
char cc[2];
int ret;
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;
}
}
cc[0] = c;
cc[1] = nextc;
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'))) {
*dst++ = c;
if (c == '\\' && (flag & VIS_NOSLASH) == 0)
*dst++ = '\\';
*dst = '\0';
return (dst);
}
ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, "", NULL);
if (ret < 0)
return NULL;
return mbdst + ret;
}
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;
}
}
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;
}
if ((flag & VIS_NOSLASH) == 0)
*dst++ = '\\';
if (c & 0200) {
c &= 0177;
*dst++ = 'M';
}
if (iscntrl(c)) {
*dst++ = '^';
if (c == 0177)
*dst++ = '?';
else
*dst++ = c + '@';
} else {
*dst++ = '-';
*dst++ = c;
}
done:
*dst = '\0';
return (dst);
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, strnvis, strvisx - visually encode characters from src into dst
* 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.
*/
int
strvis(char *mbdst, const char *mbsrc, int flags)
{
return istrsenvisxl(&mbdst, NULL, mbsrc, flags, "", NULL);
}
int
strnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags)
{
return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, "", NULL);
}
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.
*
* 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.
* Strvisx encodes exactly len characters 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
strvisx(char *mbdst, const char *mbsrc, size_t len, int flags)
{
return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, "", NULL);
}
int
strnvis(char *dst, const char *src, size_t siz, int flag)
strnvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags)
{
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)) {
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);
return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", NULL);
}
int
strvisx(char *dst, const char *src, size_t len, int flag)
strenvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
int *cerr_ptr)
{
int 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);
return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", cerr_ptr);
}