#include "config.h"
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/4/93";
#else
__RCSID("$NetBSD: unvis.c,v 1.30 2009/02/11 13:51:59 christos Exp $");
#endif
#endif
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <vis.h>
#ifdef __weak_alias
__weak_alias(strunvis,_strunvis)
#endif
#if !HAVE_VIS
#define S_GROUND 0
#define S_START 1
#define S_META 2
#define S_META1 3
#define S_CTRL 4
#define S_OCTAL2 5
#define S_OCTAL3 6
#define S_HEX1 7
#define S_HEX2 8
#define S_MIME1 9
#define S_MIME2 10
#define S_EATCRNL 11
#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))
int
unvis(char *cp, int c, int *astate, int flag)
{
unsigned char uc = (unsigned char)c;
_DIAGASSERT(cp != NULL);
_DIAGASSERT(astate != NULL);
if (flag & UNVIS_END) {
if (*astate == S_OCTAL2 || *astate == S_OCTAL3
|| *astate == S_HEX2) {
*astate = S_GROUND;
return UNVIS_VALID;
}
return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD);
}
switch (*astate) {
case S_GROUND:
*cp = 0;
if (c == '\\') {
*astate = S_START;
return UNVIS_NOCHAR;
}
if ((flag & VIS_HTTPSTYLE) && c == '%') {
*astate = S_HEX1;
return UNVIS_NOCHAR;
}
if ((flag & VIS_MIMESTYLE) && c == '=') {
*astate = S_MIME1;
return UNVIS_NOCHAR;
}
*cp = c;
return UNVIS_VALID;
case S_START:
switch(c) {
case '\\':
*cp = c;
*astate = 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 UNVIS_NOCHAR;
case 'M':
*cp = (char)0200;
*astate = S_META;
return UNVIS_NOCHAR;
case '^':
*astate = S_CTRL;
return UNVIS_NOCHAR;
case 'n':
*cp = '\n';
*astate = S_GROUND;
return UNVIS_VALID;
case 'r':
*cp = '\r';
*astate = S_GROUND;
return UNVIS_VALID;
case 'b':
*cp = '\b';
*astate = S_GROUND;
return UNVIS_VALID;
case 'a':
*cp = '\007';
*astate = S_GROUND;
return UNVIS_VALID;
case 'v':
*cp = '\v';
*astate = S_GROUND;
return UNVIS_VALID;
case 't':
*cp = '\t';
*astate = S_GROUND;
return UNVIS_VALID;
case 'f':
*cp = '\f';
*astate = S_GROUND;
return UNVIS_VALID;
case 's':
*cp = ' ';
*astate = S_GROUND;
return UNVIS_VALID;
case 'E':
*cp = '\033';
*astate = S_GROUND;
return UNVIS_VALID;
case '\n':
*astate = S_GROUND;
return (UNVIS_NOCHAR);
case '$':
*astate = S_GROUND;
return (UNVIS_NOCHAR);
}
*astate = S_GROUND;
return (UNVIS_SYNBAD);
case S_META:
if (c == '-')
*astate = S_META1;
else if (c == '^')
*astate = S_CTRL;
else {
*astate = S_GROUND;
return (UNVIS_SYNBAD);
}
return UNVIS_NOCHAR;
case S_META1:
*astate = S_GROUND;
*cp |= c;
return UNVIS_VALID;
case S_CTRL:
if (c == '?')
*cp |= 0177;
else
*cp |= c & 037;
*astate = S_GROUND;
return UNVIS_VALID;
case S_OCTAL2:
if (isoctal(uc)) {
*cp = (*cp << 3) + (c - '0');
*astate = S_OCTAL3;
return UNVIS_NOCHAR;
}
*astate = S_GROUND;
return UNVIS_VALIDPUSH;
case S_OCTAL3:
*astate = S_GROUND;
if (isoctal(uc)) {
*cp = (*cp << 3) + (c - '0');
return UNVIS_VALID;
}
return UNVIS_VALIDPUSH;
case S_HEX1:
if (isxdigit(uc)) {
*cp = xtod(uc);
*astate = S_HEX2;
return UNVIS_NOCHAR;
}
*astate = S_GROUND;
return UNVIS_VALIDPUSH;
case S_HEX2:
*astate = S_GROUND;
if (isxdigit(uc)) {
*cp = xtod(uc) | (*cp << 4);
return UNVIS_VALID;
}
return UNVIS_VALIDPUSH;
case S_MIME1:
if (uc == '\n' || uc == '\r') {
*astate = S_EATCRNL;
return UNVIS_NOCHAR;
}
if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) {
*cp = XTOD(uc);
*astate = S_MIME2;
return UNVIS_NOCHAR;
}
*astate = S_GROUND;
return UNVIS_SYNBAD;
case S_MIME2:
if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) {
*astate = S_GROUND;
*cp = XTOD(uc) | (*cp << 4);
return UNVIS_VALID;
}
*astate = S_GROUND;
return UNVIS_SYNBAD;
case S_EATCRNL:
switch (uc) {
case '\r':
case '\n':
return UNVIS_NOCHAR;
case '=':
*astate = S_MIME1;
return UNVIS_NOCHAR;
default:
*cp = uc;
return UNVIS_VALID;
}
default:
*astate = S_GROUND;
return UNVIS_SYNBAD;
}
}
int
strunvisx(dst, src, flag)
char *dst;
const char *src;
int flag;
{
char c;
char *start = dst;
int state = 0;
_DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
while ((c = *src++) != '\0') {
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 (int)(dst - start);
}
int
strunvis(dst, src)
char *dst;
const char *src;
{
return strunvisx(dst, src, 0);
}
#endif