// escapes.c

#include "fetchmail.h"

#include <string.h>

/** Process standard C-style escape sequences in a string,
 * this can never lengthen the output.
 * For octal escapes \400 to \777,
 * this implementation silently converts to char.
 */
void escapes(char *cp /** string with escapes, modified in place */)
{
    char *ep = cp + strlen(cp); // original NUL byte
    char *tp = cp;  /* write pointer same as read pointer initially, no aliasing violation because it's char* */
    while (*cp)
    {
	int	cval = 0;

	/* we MUST check for NUL explicitly, as strchr(string, 0) will
	 * always succeed! */
	if (*cp == '\\') {
		if (cp[1] && strchr("01234567xX", cp[1])) /* octal or hexadecimal escape */
		{
		    const char *dp;
		    const char hex[] = "00112233445566778899aAbBcCdDeEfF";
		    const char oct[] = "01234567";
		    unsigned dcount = 0;

		    if (*++cp == 'x' || *cp == 'X')
			for (++cp; *cp && (dp = strchr(hex, *cp)) && (dcount++ < 2); cp++)
			    cval = (cval * 16) + (int)(dp - hex) / 2; /* cast from ptrdiff_t to int */
		    else if (strchr(oct, *cp))
			while (*cp && strchr(oct, *cp) && (dcount++ < 3))
			    cval = (cval * 8) + (*cp++ - '0');
		    else
			while (*cp && (strchr("0123456789",*cp)!=(char*)NULL)&&(dcount++ < 3))
			    cval = (cval * 10) + (*cp++ - '0');
		}
		else /* C-style character escapes */
		{
		    switch (*++cp)
		    {
		    case '\0': goto done;	   /* ignore backslash at file end */
		    case '\n': cp++; continue;	/* backslash before LF to join lines */
		    // \' \" and \? handled in default 
		    case 'a':  cval = '\a'; break;
		    case 'b':  cval = '\b'; break;
		    case 'f':  cval = '\f'; break;
		    case 'n':  cval = '\n'; break;
		    case 'r':  cval = '\r'; break;
		    case 't':  cval = '\t'; break;
		    case 'v':  cval = '\v'; break;
		    default:   cval = *cp;  // identity, drop backslash
		    }
		    cp++;
		}
	} else {
	    // no backslash sequence, copy
	    cval = *cp++;
	}
	*tp++ = (char)cval;
    }
done:
    if (tp < ep) { // string was shortened
	    memset(tp, '\0', (size_t)(ep - tp));
    }
}
