blob: bf4f9d31e8b99bd169788d9f4f5c5c408e2033ae [file] [log] [blame]
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include "unescape_c_string.h"
#ifndef isoctal
# define isoctal(c) ((c) >= '0' && (c) <= '7')
#endif
#ifndef digittoint
# define digittoint(c) (((c) >= '0' && (c) <= '9') ? (c) - '0' : tolower(c) - 'a' + 10)
#endif
typedef enum C_STRING_STATE {
C_STRING_STATE_GROUND,
C_STRING_STATE_START,
C_STRING_STATE_OCTAL2,
C_STRING_STATE_OCTAL3,
C_STRING_STATE_HEX1,
C_STRING_STATE_HEX2
} C_STRING_STATE;
int
unescape_c_string(char *cp, char c, int *astate, int flag)
{
if (flag & C_STRING_FLAG_END) {
switch (*astate) {
case C_STRING_STATE_OCTAL2:
case C_STRING_STATE_OCTAL3:
case C_STRING_STATE_HEX1:
case C_STRING_STATE_HEX2:
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case C_STRING_STATE_GROUND:
return C_STRING_RESULT_NOCHAR;
default:
return C_STRING_RESULT_SYNBAD;
}
}
switch (*astate) {
case C_STRING_STATE_GROUND:
*cp = 0;
if (c == '\\') {
*astate = C_STRING_STATE_START;
return C_STRING_RESULT_PENDING;
}
*cp = c;
return C_STRING_RESULT_VALID;
case C_STRING_STATE_START:
*cp = 0;
switch (c) {
case '\\':
*cp = c;
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
*cp = (c - '0');
*astate = C_STRING_STATE_OCTAL2;
return C_STRING_RESULT_PENDING;
case 'x':
*astate = C_STRING_STATE_HEX1;
return C_STRING_RESULT_PENDING;
case 'n':
*cp = '\n';
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case 'r':
*cp = '\r';
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case 'b':
*cp = '\b';
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case 'a':
*cp = '\a';
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case 'v':
*cp = '\v';
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case 't':
*cp = '\t';
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case 'f':
*cp = '\f';
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case 's':
*cp = ' ';
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case 'E':
*cp = '\033';
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALID;
case '\n':
case '$':
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_NOCHAR;
}
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_SYNBAD;
case C_STRING_STATE_OCTAL2:
if (isoctal((int) (unsigned char) c)) {
*cp = (*cp << 3) + (c - '0');
*astate = C_STRING_STATE_OCTAL3;
return C_STRING_RESULT_PENDING;
}
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALIDPUSH;
case C_STRING_STATE_OCTAL3:
*astate = C_STRING_STATE_GROUND;
if (isoctal((int) (unsigned char) c)) {
*cp = (*cp << 3) + (c - '0');
return C_STRING_RESULT_VALID;
}
return C_STRING_RESULT_VALIDPUSH;
case C_STRING_STATE_HEX1:
if (isxdigit((int) (unsigned char) c)) {
*cp = digittoint((int) (unsigned char) c);
*astate = C_STRING_STATE_HEX2;
return C_STRING_RESULT_PENDING;
}
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_VALIDPUSH;
case C_STRING_STATE_HEX2:
*astate = C_STRING_STATE_GROUND;
if (isxdigit((int) (unsigned char) c)) {
*cp = (*cp << 4) + digittoint((int) (unsigned char) c);
return C_STRING_RESULT_VALID;
}
return C_STRING_RESULT_VALIDPUSH;
default:
*astate = C_STRING_STATE_GROUND;
return C_STRING_RESULT_SYNBAD;
}
}
int
str_unescape_c_string(char *dst, const char *src)
{
char c;
char *start = dst;
int state = 0;
while ((c = *src++)) {
again:
switch (unescape_c_string(dst, c, &state, 0)) {
case C_STRING_RESULT_VALID:
dst++;
break;
case C_STRING_RESULT_VALIDPUSH:
dst++;
goto again;
case C_STRING_RESULT_PENDING:
case C_STRING_RESULT_NOCHAR:
break;
default:
*dst = 0;
return -1;
}
}
if (unescape_c_string(dst, c, &state, C_STRING_FLAG_END) == C_STRING_RESULT_VALID) {
dst++;
}
*dst = 0;
return dst - start;
}
ssize_t
strn_unescape_c_string(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 (unescape_c_string(&p, c, &state, 0)) {
case C_STRING_RESULT_VALID:
if (dst < end) {
*dst = p;
}
dst++;
break;
case C_STRING_RESULT_VALIDPUSH:
if (dst < end) {
*dst = p;
}
dst++;
goto again;
case 0:
case C_STRING_RESULT_NOCHAR:
break;
default:
if (dst <= end) {
*dst = 0;
}
return -1;
}
}
if (unescape_c_string(&p, c, &state, C_STRING_FLAG_END) == C_STRING_RESULT_VALID) {
if (dst < end) {
*dst = p;
}
dst++;
}
if (dst <= end) {
*dst = 0;
}
return dst - start;
}