PRIVATE char *intlmime_decode_qp(char *in)
{
int i = 0, length;
char token[3];
char *out, *dest = 0;
out = dest = (char *)XP_ALLOC(strlen(in)+1);
if (dest == NULL)
return NULL;
memset(out, 0, strlen(in)+1);
length = strlen(in);
while (length > 0 || i != 0)
{
while (i < 3 && length > 0)
{
token [i++] = *in;
in++;
length--;
}
if (i < 3)
{
/* Didn't get enough for a complete token.
If it might be a token, unread it.
Otherwise, just dump it.
*/
strncpy (out, token, i);
break;
}
i = 0;
if (token [0] == '=')
{
unsigned char c = 0;
if (token[1] >= '0' && token[1] <= '9')
c = token[1] - '0';
else if (token[1] >= 'A' && token[1] <= 'F')
c = token[1] - ('A' - 10);
else if (token[1] >= 'a' && token[1] <= 'f')
c = token[1] - ('a' - 10);
else if (token[1] == CR || token[1] == LF)
{
/* =\n means ignore the newline. */
if (token[1] == CR && token[2] == LF)
; /* swallow all three chars */
else
{
in--; /* put the third char back */
length++;
}
continue;
}
else
{
/* = followed by something other than hex or newline -
pass it through unaltered, I guess. (But, if
this bogus token happened to occur over a buffer
boundary, we can't do this, since we don't have
space for it. Oh well. Forget it.) */
if (in > out) *out++ = token[0];
if (in > out) *out++ = token[1];
if (in > out) *out++ = token[2];
continue;
}
/* Second hex digit */
c = (c << 4);
if (token[2] >= '0' && token[2] <= '9')
c += token[2] - '0';
else if (token[2] >= 'A' && token[2] <= 'F')
c += token[2] - ('A' - 10);
else if (token[2] >= 'a' && token[2] <= 'f')
c += token[2] - ('a' - 10);
else
{
/* We got =xy where "x" was hex and "y" was not, so
treat that as a literal "=", x, and y. (But, if
this bogus token happened to occur over a buffer
boundary, we can't do this, since we don't have
space for it. Oh well. Forget it.) */
if (in > out) *out++ = token[0];
if (in > out) *out++ = token[1];
if (in > out) *out++ = token[2];
continue;
}
*out++ = (char) c;
}
else
{
*out++ = token [0];
token[0] = token[1];
token[1] = token[2];
i = 2;
}
}
/* take care of special underscore case */
for (out = dest; *out; out++)
if (*out == '_') *out = ' ';
return dest;
}