mirror of
https://gitlab.freedesktop.org/libbsd/libbsd.git
synced 2025-10-23 00:08:02 +02:00
Update vis/unvis modules from NetBSD
This commit is contained in:
@@ -144,4 +144,16 @@ LIBBSD_0.9 {
|
||||
flopenat;
|
||||
|
||||
pidfile_fileno;
|
||||
|
||||
nvis;
|
||||
snvis;
|
||||
stravis;
|
||||
strenvisx;
|
||||
strnunvisx;
|
||||
strsenvisx;
|
||||
strsnvis;
|
||||
strsnvisx;
|
||||
strsvis;
|
||||
strsvisx;
|
||||
svis;
|
||||
} LIBBSD_0.8;
|
||||
|
544
src/unvis.c
544
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);
|
||||
}
|
||||
|
||||
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
876
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.
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user