/* source: xio-ascii.c */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* Published under the GNU General Public License V.2, see file COPYING */

/* this file contains functions for text encoding, decoding, and conversions */


#include <stddef.h>
#include <ctype.h>
#include <stdio.h>

#include "xio-ascii.h"

/* For each 6 bit pattern we have an ASCII character in the array */
const static int base64chars[] = {
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
  'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
  'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  'w', 'x', 'y', 'z', '0', '1', '2', '3',
  '4', '5', '6', '7', '8', '9', '+', '/',
} ;

#define CHAR64(c) (base64chars[c])

char *
   xiob64encodeline(const char *data,		/* input data */
		    size_t bytes,		/* length of input data, >=0 */
		    char *coded	/* output buffer, must be long enough */
		 ) {
   int c1, c2, c3;

   while (bytes > 0) {
      c1 = *data++;
      *coded++ = CHAR64(c1>>2);
      if (--bytes == 0) {
	 *coded++ = CHAR64((c1&0x03)<<4);
	 *coded++ = '=';
	 *coded++ = '=';
      } else {
	 c2 = *data++;
	 *coded++ = CHAR64(((c1&0x03)<<4)|(c2>>4));
	 if (--bytes == 0) {
	    *coded++ = CHAR64((c2&0x0f)<<2);
	    *coded++ = '=';
	 } else {
	    c3 = *data++; --bytes;
	    *coded++ = CHAR64(((c2&0x0f)<<2)|(c3>>6));
	    *coded++ = CHAR64(c3&0x3f);
	 }
      }
   }
   return coded;
}



/* sanitize "untrusted" text, replacing special control characters with the C
   string version ("\x"), and replacing unprintable chars with ".".
   text can grow to double size, so keep output buffer long enough!
   returns a pointer to the first untouched byte of the output buffer.
*/
char *xiosanitize(const char *data,	/* input data */
		  size_t bytes,			/* length of input data, >=0 */
		  char *coded	/* output buffer, must be long enough */
		  ) {
   int c;

   while (bytes > 0) {
      c = *(unsigned char *)data++;
      switch (c) {
      case '\0' : *coded++ = '\\'; *coded++ = '0'; break;
      case '\a' : *coded++ = '\\'; *coded++ = 'a'; break;
      case '\b' : *coded++ = '\\'; *coded++ = 'b'; break;
      case '\t' : *coded++ = '\\'; *coded++ = 't'; break;
      case '\n' : *coded++ = '\\'; *coded++ = 'n'; break;
      case '\v' : *coded++ = '\\'; *coded++ = 'v'; break;
      case '\f' : *coded++ = '\\'; *coded++ = 'f'; break;
      case '\r' : *coded++ = '\\'; *coded++ = 'r'; break;
      case '\'' : *coded++ = '\\'; *coded++ = '\''; break;
      case '\"' : *coded++ = '\\'; *coded++ = '"'; break;
      case '\\' : *coded++ = '\\'; *coded++ = '\\'; break;
      default:
	 if (!isprint(c))
	    c = '.';
	 *coded++ = c;
	 break;
      }
      --bytes;
   }
   return coded;
}


/* print the bytes in hex */
char *
   xiohexdump(const unsigned char *data, size_t bytes, char *coded) {
   int space = 0;
   while (bytes-- > 0) {
      if (space)  { *coded++ = ' '; }
      coded += sprintf(coded, "%02x", *data++);
      space = 1;
   }
   return coded;
}

/* write the binary data to output buffer codbuff in human readable form.
   bytes gives the length of the data, codlen the available space in codbuff.
   coding specifies how the data is to be presented. Not much to select now.
   returns a pointer to the first char in codbuff that has not been overwritten;
   it might also point to the first char after the buffer!
   this function does not write a terminating \0
*/
static char *
_xiodump(const unsigned char *data, size_t bytes, char *codbuff, size_t codlen,
	 int coding) {
   int start = 1;
   int space = coding & 0xff;

   if (bytes <= 0)  { return codbuff; }
   if (codlen < 1)  { return codbuff; }
   if (space == 0) space = -1;
   if (0) {
      ;	/* for canonical reasons */
   } else if (1) {
      /* simple hexadecimal output */
      if (3*bytes+1 > codlen) {
	 bytes = (codlen-1)/3;	/* "truncate" data so generated text fits */
      }
      *codbuff++ = 'x';
      while (bytes-- > 0) {
	 if (start == 0 && space == 0) {
	    *codbuff++ = ' ';
	    space = (coding & 0xff);
	 }
	 codbuff += sprintf(codbuff, "%02x", *data++);
	 start = 0;
      }
   }
   return codbuff;
}

/* write the binary data to codbuff in human readable form.
   bytes gives the length of the data, codlen the available space in codbuff.
   coding specifies how the data is to be presented. Not much to select now.
   null terminates the output. returns a pointer to the output string.
*/
char *
xiodump(const unsigned char *data, size_t bytes, char *codbuff, size_t codlen,
	int coding) {
   char *result;

   result = _xiodump(data, bytes, codbuff, codlen-1, coding);
   *result = '\0';
   return codbuff;
}
