/*
Copyright (c) 2000-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/



/**	@file	dkenc.c	Encoding module.
*/



#include "dk.h"
#include "dktypes.h"
#include "dkmem.h"
#include "dkerror.h"
#include "dkma.h"

#if DK_HAVE_CTYPE_H
#include <ctype.h>
#endif


/**	Inside the dkenc module.
*/
#define DK_ENC_C 1

#include "dkenc.h"




#line 64 "dkenc.ctr"




/**	Abbreviation for use in prototypes.
*/
typedef unsigned long UL;

/**	Abbreviation for use in prototypes.
*/
typedef unsigned short US;

/**	Abbreviation for use in prototypes.
*/
typedef unsigned char UC;



/** Factors to add ASCII85-digits to byte quadrupel.
*/
static unsigned long f2[] = {
  1UL, 85UL, (85UL * 85UL), (85UL * 85UL * 85UL), (85UL * 85UL * 85UL * 85UL)
};



static unsigned long last_byte = 0x000000FFUL;


#if !DK_WORDS_BIGENDIAN


/**	Swap unsigned long (change byte order).
	@param	x	Value to convert.
	@return	Conversion result.
*/
static unsigned long	swapul	DK_P1(unsigned long, x)
{
  unsigned long back;
  unsigned char *uc1, *uc2;
  size_t i;
  
  uc1 = (unsigned char *)(&x);
  uc2 = (unsigned char *)(&back);
  for(i = 0; i < sizeof(UL); i++) {
    uc2[i] = uc1[(sizeof(UL) - 1 - i)];
  } 
  return back;
}



/**	Swap unsigned short (change byte order).
	@param	x	Value to convert.
	@return	Conversion result.
*/
static
unsigned short
swapus	DK_P1(unsigned short, x)
{
  unsigned short back;
  unsigned char *uc1, *uc2;
  size_t i;
  
  uc1 = (unsigned char *)(&x);
  uc2 = (unsigned char *)(&back);
  for(i = 0; i < sizeof(US); i++) {
    uc2[i] = uc1[(sizeof(US) - 1 - i)];
  } 
  return back;
}

#endif
/* if !DK_WORDS_BIGENDIAN */


unsigned long dkenc_ntohl	DK_P1(unsigned long, x)
{
  unsigned long back;
  
#if DK_WORDS_BIGENDIAN
  back = x;		
#else
  back = swapul(x);	
#endif
  
  return back;
}



unsigned long dkenc_htonl	DK_P1(unsigned long, x)
{
  unsigned long back;
  
#if DK_WORDS_BIGENDIAN
  back = x;		
#else
  back = swapul(x);	
#endif
  
  return back;
}



unsigned short
dkenc_ntohs DK_P1(unsigned short, x)
{
  unsigned short back;
  
#if DK_WORDS_BIGENDIAN
  back = x;		
#else
  back = swapus(x);	
#endif
  
  return back;
}



unsigned short
dkenc_htons DK_P1(unsigned short, x)
{
  unsigned short back;
  
#if DK_WORDS_BIGENDIAN
  back = x;		
#else
  back = swapus(x);	
#endif
  
  return back;
}



/**	Convert 32-bit character to UTF-8.
	@param	c	The 32-bit character.
	@param	u8p	Buffer to receive UTF-8 encoded result.
	@param	u8l	Length of \a u8l.
	@return	The number of bytes produced.
*/
size_t
dkenc_uc2utf8 DK_P3(dk_udword, c, dk_ubyte *, u8p, size_t, u8l)
{
  size_t back = 0;
  dk_udword x;
  
  if(u8p) {
  if(u8l > 0) {
    if(c > 0x7FUL) {
      if(c > 0x000007FFUL) {
	if(c > 0x0000FFFFUL) {
	  if(c > 0x001FFFFFUL) {
	    if(c > 0x03FFFFFFUL) {
	      if(c < 0x80000000UL) {		/* 6 Byte */
		if(u8l >= 6) {
		  back = 6;
		  x = c >> 30;
		  x &= 0x00000001UL;
		  x |= 0x000000FCUL;
		  u8p[0] = (dk_ubyte)x;
		  x = c >> 24; x &= 0x0000003FUL; x |= 0x00000080UL;
		  u8p[1] = (dk_ubyte)x;
		  x = c >> 18; x &= 0x0000003FUL; x |= 0x00000080UL;
		  u8p[2] = (dk_ubyte)x;
		  x = c >> 12; x &= 0x0000003FUL; x |= 0x00000080UL;
		  u8p[3] = (dk_ubyte)x;
		  x = c >>  6; x &= 0x0000003FUL; x |= 0x00000080UL;
		  u8p[4] = (dk_ubyte)x;
		  x = c & 0x0000003FUL; x |= 0x00000080UL;
		  u8p[5] = (dk_ubyte)x;
		}
	      }
	    } else {				/* 5 Byte */
	      if(u8l >= 5) {
		back = 5;
		x = c >> 24;
		x &= 0x00000003UL;
		x |= 0x000000F8UL;
		u8p[0] = (dk_ubyte)x;
		x = c >> 18; x &= 0x0000003FUL; x |= 0x00000080UL;
		u8p[1] = (dk_ubyte)x;
		x = c >> 12; x &= 0x0000003FUL; x |= 0x00000080UL;
		u8p[2] = (dk_ubyte)x;
		x = c >>  6; x &= 0x0000003FUL; x |= 0x00000080UL;
		u8p[3] = (dk_ubyte)x;
		x = c & 0x0000003FUL; x |= 0x00000080UL;
		u8p[4] = (dk_ubyte)x;
	      }
	    }
	  } else {				/* 4 Byte */
	    if(u8l >= 4) {
	      back = 4;
	      x = c >> 18;
	      x &= 0x00000007UL;
	      x |= 0x000000F0UL;
	      u8p[0] = (dk_ubyte)x;
	      x = c >> 12;
	      x &= 0x0000003FUL;
	      x |= 0x00000080UL;
	      u8p[1] = (dk_ubyte)x;
	      x = c >>  6;
	      x &= 0x0000003FUL;
	      x |= 0x00000080UL;
	      u8p[2] = (dk_ubyte)x;
	      x = c & 0x0000003FUL;
	      x |= 0x00000080UL;
	      u8p[3] = (dk_ubyte)x;
	    }
	  }
	} else {				/* 3 Byte */
	  if(u8l >= 3) {
	    back = 3;
	    x = c >> 12;
	    x &= 0x0000000FUL;
	    x |= 0x000000E0UL;
	    u8p[0] = (dk_ubyte)x;
	    x = c >>  6;
	    x &= 0x0000003FUL;
	    x |= 0x00000080UL;
	    u8p[1] = (dk_ubyte)x;
	    x = c & 0x0000003FUL;
	    x |= 0x00000080UL;
	    u8p[2] = (dk_ubyte)x;
	  }
	}
      } else {					/* 2 Byte */
	if(u8l >= 2) {
	  back = 2;
	  x = c >> 6;
	  x &= 0x0000001FUL;
	  x |= 0x000000C0UL;
	  u8p[0] = (dk_ubyte)x;
	  x = c & 0x0000003FUL;
	  x |= 0x00000080UL;
	  u8p[1] = (dk_ubyte)x;
	}
      }
    } else {					/* 1 Byte */
      if(u8l >= 1) {
	back = 1;
	u8p[0] = (dk_ubyte)(c & 0x0000007FUL);
      }
    }
  }
  } 
  return back;
}



/**	Convert UTF-8 encoded text to 32-bit character.
	@param	ucp	Pointer to variable for result.
	@param	u8p	Buffer containing UTF-8 encoded text.
	@param	u8l	Number of bytes in buffer.
	@param	u8u	Pointer to variable to receive number of bytes used.
	@return	1 on success, 0 on error.
*/
int
dkenc_utf82uc DK_P4(dk_udword *, ucp, dk_ubyte *, u8p, size_t , u8l, size_t *, u8u)
{
  int back = 0;
  dk_ubyte	u1;
  dk_udword res, x1, x2, x3, x4, x5, x6;
  size_t needed_length;
  
  needed_length = 0;
  if(ucp) {
  if(u8p) {
  if(u8l) {
  if(u8u) {
  if((u8l) > 0) {
    u1 = *u8p; needed_length = 1;
    if(u1 > 0x7F) {					/* > 1 Byte */
      if((u1 & 0xE0) == 0xC0) {				/* 2 Byte */
	needed_length = 2;
	if(u8l >= needed_length) {
	if((u8p[1] & 0xC0) == 0x80) {
	  x1 = u1;
	  x1 = x1 << 6;
	  x1 = x1 & 0x000007C0UL;
	  x2 = u8p[1];
	  x2 = x2 & 0x0000003FUL;
	  res = (x1 | x2); back = 1;
	}
	}
      } else {
	if((u1 & 0xF0) == 0xE0) {			/* 3 Byte */
	  needed_length = 3;
	  if(u8l >= needed_length) {
	    if((u8p[1] & 0xC0) == 0x80) {
	    if((u8p[2] & 0xC0) == 0x80) {
	      x1 = u1;
	      x1 = x1 << 12;
	      x1 = x1 & 0x0000F000UL;
	      x2 = u8p[1];
	      x2 = x2 << 6;
	      x2 = x2 & 0x00000FC0UL;
	      x3 = u8p[2];
	      x3 = x3 & 0x0000003FUL;
	      res = (x1 | x2 | x3); back = 1;
	    }
	    }
	  }
	} else {
	  if((u1 & 0xF8) == 0xF0) {			/* 4 Byte */
	    needed_length = 4;
	    if(u8l >= needed_length) {
	      if((u8p[1] & 0xC0) == 0x80) {
	      if((u8p[2] & 0xC0) == 0x80) {
	      if((u8p[3] & 0xC0) == 0x80) {
		x1 = u1;
		x2 = u8p[1];
		x3 = u8p[2];
		x4 = u8p[3];
		x1 = x1 << 18;
		x1 = x1 & 0x001C0000UL;
		x2 = x2 << 12;
		x2 = x2 & 0x0003F000UL;
		x3 = x3 << 6;
		x3 = x3 & 0x00000FC0UL;
		x4 = x4 & 0x0000003FUL;
		res = (x1 | x2 | x3 | x4); back = 1;
	      }
	      }
	      }
	    }
	  } else {
	    if((u1 & 0xFC) == 0xF8) {			/* 5 Byte */
	      needed_length = 5;
	      if(u8l >= needed_length) {
		if((u8p[1] & 0xC0) == 0x80) {
		if((u8p[2] & 0xC0) == 0x80) {
		if((u8p[3] & 0xC0) == 0x80) {
		if((u8p[4] & 0xC0) == 0x80) {
		  x1 = u1;
		  x2 = u8p[1];
		  x3 = u8p[2];
		  x4 = u8p[3];
		  x5 = u8p[4];
		  x1 = x1 << 24;
		  x1 = x1 & 0x03000000UL;
		  x2 = x2 << 18;
		  x2 = x2 & 0x00FC0000UL;
		  x3 = x3 << 12;
		  x3 = x3 & 0x0003F000UL;
		  x4 = x4 <<  6;
		  x4 = x4 & 0x00000FC0UL;
		  x5 = x5 & 0x0000003FUL;
		  res = (x1 | x2 | x3 | x4 | x5); back = 1;
		}
		}
		}
		}
	      }
	    } else {
	      if((u1 & 0xFE) == 0xFC) {			/* 6 Byte */
		needed_length = 6;
		if(u8l >= needed_length) {
		  if((u8p[1] & 0xC0) == 0x80) {
		  if((u8p[2] & 0xC0) == 0x80) {
		  if((u8p[3] & 0xC0) == 0x80) {
		  if((u8p[4] & 0xC0) == 0x80) {
		  if((u8p[5] & 0xC0) == 0x80) {
		    x1 = 0x40000000UL;
		    x2 = u8p[1]; x3 = u8p[2]; x4 = u8p[3];
		    x5 = u8p[4]; x6 = u8p[5];
		    x2 = x2 << 24;
		    x3 = x3 << 18;
		    x4 = x4 << 12;
		    x5 = x5 <<  6;
		    x2 = x2 & 0x3F000000UL;
		    x3 = x3 & 0x00FC0000UL;
		    x4 = x4 & 0x0003F000UL;
		    x5 = x5 & 0x00000FC0UL;
		    x6 = x6 & 0x0000003FUL;
		    res = (x1 | x2 | x3 | x4 | x5 | x6);
		    back = 1;
		  }
		  }
		  }
		  }
		  }
		}
	      }
	    }
	  }
	}
      }
    } else {						/* 1 Byte */
      res = u1;
      back = 1;
    }
  }
  }
  }
  }
  }
  if(back) {
    *u8u = needed_length;
    *ucp = res;
  } 
  return back;
}



#if !DK_HAVE_ISDIGIT
/**	Check whether a character is a digit.
	@param	c	Character to check.
	@return	1 on success, 0 on error.
*/
static int
my_is_digit DK_P1(char,c)
{
  int back = 0;
  switch(c) {
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
    {
      back = 1;
    }
    break;
  }
  return back;
}
#endif



/**	Convert digit to corresponding unsigned long.
	@param	c	Digit.
	@return	Unsigned long for the digit.
*/
static unsigned long char_to_ul DK_P1(char, c)
{
  unsigned long back = 0UL;
  if(c == '1') back = 1UL;
  if(c == '2') back = 2UL;
  if(c == '3') back = 3UL;
  if(c == '4') back = 4UL;
  if(c == '5') back = 5UL;
  if(c == '6') back = 6UL;
  if(c == '7') back = 7UL;
  if(c == '8') back = 8UL;
  if(c == '9') back = 9UL;
  return back;
}



int
dkenc_ipaddr_to_ul DK_P2(char *,str,unsigned long *,ul)
{
  int back = 0;
  int state; char *ptr;
  unsigned long ul1, ul2, ul3, ulval;
  
  if(str && ul) {
    state = 0; ptr = str; back = 1;
    ul1 = ul2 = ul3 = ulval = 0UL;
    while(back && (*ptr)) {
      
      if(isdigit(*ptr)) {
        
	switch(state) {
	  case 0:
	  case 1:
	  case 2:
	  case 4:
	  case 5:
	  case 6:
	  case 8:
	  case 9:
	  case 10:
	  case 12:
	  case 13:
	  case 14: {
	    ulval = 10UL * ulval + char_to_ul(*ptr); state++;
	    
	  } break;
	  default: {
	    back = 0;
	  } break;
	}
      } else {
        if(*ptr == '.') {
	  
	  switch(state) {
	    case 0:
	    case 1:
	    case 2:
	    case 3: {
	      ul1 = ulval; ulval = 0UL; state = 4;
	    } break;
	    case 4:
	    case 5:
	    case 6:
	    case 7: {
	      ul2 = ulval; ulval = 0UL; state = 8;
	    } break;
	    case 8:
	    case 9:
	    case 10:
	    case 11: {
	      ul3 = ulval; ulval = 0UL; state = 12;
	    } break;
	  }
	} else {
	  back = 0; 
	}
      }
      ptr++;
    }
    if((state < 12) || (state > 15)) {
      back = 0;
      
    }
    if(back) {
      if(ul1 > 255UL) back = 1;
      if(ul2 > 255UL) back = 1;
      if(ul3 > 255UL) back = 1;
      if(ulval > 255UL) back = 1;
    }
    if(back) {
      ul1 = ul1 << 24;
      ul1 &= 0xFF000000UL;
      ul2 = ul2 << 16;
      ul2 &= 0x00FF0000UL;
      ul3 = ul3 <<  8;
      ul3 &= 0x0000FF00UL;
      ulval &= 0x000000FFUL;
      ulval = ulval | ul1 | ul2 | ul3;
      *ul = ulval;
    }
  }
  
  return back;
}




/*
	Convert UTF-8 encoded string to 8-bit LATIN-1.
	The result is a new string, you must release it
	using dk_delete().
*/
char *dkenc_str_utf82bits8 DK_P2(char *,s, int *,ecp)
{
  char *back = NULL;	/* function result */
  int me;		/* my error code */
  int cc;		/* can continue */
  size_t lgt;		/* string length after recoding */
  size_t sl;		/* original string length */
  size_t ni;		/* start index of byte for current conversion */
  size_t used;		/* bytes used in current conversion  */
  size_t newlength;	/* temporary value, summary of used bytes */
  dk_udword uw; dk_ubyte ub; char c;
  
  if(s) {
    me = 0; lgt = 1; sl = strlen(s); cc = 1; ni = 0;
    while((cc) && (ni < sl)) {
      used = 0;
      if(dkenc_utf82uc(&uw, (dk_ubyte *)(&(s[ni])),(sl-ni),&used)) {
        lgt++;
        newlength = ni + used;
	if(newlength > ni) {
	  ni = newlength;
	  if(newlength >= sl) {
	    cc = 0;
	  }
	} else {
	  me = 1; cc = 0;
	  if(ecp) { *ecp = DK_ERR_INVALID_ARGS; }
	}
      } else {
        cc = 0; me = 1;
	if(ecp) { *ecp = DK_ERR_INVALID_ARGS; }
      }
    } 
    if((!me)) {
      back = dk_new(char,lgt);
      if(back) { 
        ni = 0; cc = 1; lgt = 0;
        while((cc) && (ni < sl)) {
          used = 0;
	  if(dkenc_utf82uc(&uw, (dk_ubyte *)(&(s[ni])),(sl-ni),&used)) {
	    if(uw <= 0x000000FFUL) {
	      ub = (dk_ubyte)uw; c = (char)ub;
	    } else {
	      c = '.';
	       if(ecp) { *ecp = DK_ERR_MATH_OOR; }
	    }
	    back[lgt++] = c;
	    newlength = ni + used;
	    if(newlength > ni) {
	      ni = newlength;
	      if(newlength >= sl) {
	        cc = 0;
	      }
	    } else {
	      me = 1; cc = 0;
	      if(ecp) { *ecp = DK_ERR_INVALID_ARGS; }
	    }
	  } else {
	    me = 1; cc = 0;
	    if(ecp) { *ecp = DK_ERR_INVALID_ARGS; }
	  }
        }
        back[lgt] = '\0';
      } else {
        if(ecp) { *ecp = DK_ERR_NOMEM; }
      }
    }
  } else {
    if(ecp) { *ecp = DK_ERR_INVALID_ARGS; }
  }
  
  return back;
}



char *
dkenc_str_bits82utf8 DK_P1(char *,s)
{
  int me;		/* math error indicator */
  dk_ubyte buffer[16];	/* 7 would be sufficient */
  char *back = NULL;
  char c, *cptr;
  size_t lgt, lgtnew;
  dk_ubyte ub; dk_udword uw;
  
  if(s) {
    cptr = s; lgt = 1; me = 0;
    while(*cptr) {
      c = *cptr; ub = (dk_ubyte)c; uw = (dk_udword)ub;
      lgtnew = lgt + dkenc_uc2utf8(uw, buffer, 16);
      if(lgtnew >= lgt) {
        lgt = lgtnew;
      } else {
        me = 1;
      }
      cptr++;
    }
    if(!me) {
      back = dk_new(char,lgt);
      if(back) {
        size_t i, ni;
        ni = 0;
        cptr = s;
        while(*cptr) {
          c = *cptr; ub = (dk_ubyte)c; uw = (dk_udword)ub;
	  lgtnew = dkenc_uc2utf8(uw, buffer, 16);
	  for(i = 0; i < lgtnew; i++) {
	    back[ni + i] = buffer[i];
	  }
	  ni += lgtnew;
          cptr++;
        }
        back[ni] = '\0';
      }
    }
  }
  
  return back;
}



size_t
dkenc_size_bin_to_a85 DK_P1(size_t,s) {
  size_t back = 0;
  int ec = 0;
  unsigned long ul1, ul2, ul3;
  
  ul1 = (unsigned long)s;
  ul2 = ul1 % 4UL;
  ul3 = ul1 / 4UL;
  if(ul2) ul2++;			/* for last incomplete block */
  ul1 = dkma_add_ulong_ok(
    dkma_mul_ulong_ok(ul3, 5UL, &ec),
    ul2,
    &ec
  );					/* add 25 percent */
  ul1++;				/* final 0x00 byte for string */
  back = (size_t)ul1;
  if(ec) back = 0;			/* error checking */
  if((unsigned long)back != ul1) back = 0;
  
  return back;
}



/**	Convert binary data to ASCII-85 encoded string.
	@param	dp	Destination pointer.
	@param	ds	Destination size.
	@param	sp	Source pointer.
	@param	ss	Source size.
*/
static
void
do_bin_to_ra85 DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  register char			*mysp;
  register unsigned char	*mydp;
  register unsigned long	v;
  register size_t		i;
  register short		vused;
  
  mydp = (unsigned char *)dp; mysp = sp; v = 0UL; vused = 0;
  for(i = 0; i < ss; i++) {	
    switch(vused++) {
      case 3: {
        v |=
	((((unsigned long)((unsigned char)(*(mysp++)))) << 24) & 0xFF000000UL);
      } break;
      case 2: {
        v |=
	((((unsigned long)((unsigned char)(*(mysp++)))) << 16) & 0x00FF0000UL);
      } break;
      case 1: {
        v |=
	((((unsigned long)((unsigned char)(*(mysp++)))) <<  8) & 0x0000FF00UL);
      } break;
      default: {
        v |=
	((((unsigned long)((unsigned char)(*(mysp++))))      ) & last_byte);
      } break;
    }
    if(vused >= 4) {
      *(mydp++) = (unsigned char)((v % 85UL) + 33UL);
      v = v / 85UL;
      *(mydp++) = (unsigned char)((v % 85UL) + 33UL);
      v = v / 85UL;
      *(mydp++) = (unsigned char)((v % 85UL) + 33UL);
      v = v / 85UL;
      *(mydp++) = (unsigned char)((v % 85UL) + 33UL);
      v = v / 85UL;
      *(mydp++) = (unsigned char)((v % 85UL) + 33UL);
      vused = 0; v = 0UL;
    }
  }
  if(vused) {
    vused++;
    while(vused--) {
      *(mydp++) = (unsigned char)((v % 85UL) + 33UL);
      v = v / 85UL;
    }
  }
  *mydp = '\0';	
}



int
dkenc_bin_to_ra85 DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  int back = 0;
  size_t needed_size;
  
  if((dp) && (sp) && (ds) && (ss)) {
    needed_size = dkenc_size_bin_to_a85(ss);
    if(needed_size) {
      if(ds >= needed_size) {
        do_bin_to_ra85(dp, ds, sp, ss);
	back = 1;
      }
    }
  } 
  return back;
}



size_t
dkenc_size_a85_to_bin DK_P1(size_t,s) {
  size_t back = 0;
  unsigned long ul1, ul2, ul3;
  
  ul1 = (unsigned long)s;
  ul2 = ul1 % 5UL;
  ul3 = ul1 / 5UL;
  ul1 = 4UL * ul3 + ul2;
  back = (size_t)ul1;
  
  return back;
}



/**	Check whether a character is from the ASCII-85 charset.
	@param	c	Character to check.
	@return	1 on success, 0 on error.
*/
static
int
is_a85 DK_P1(char,c) {
  int back = 1;
  if((int)c < 33) {
    back = 0;
  } else {
    if((int)c > 117) {
      back = 0;
    }
  }
  return back;
}



/**	Convert reverse ASCII-85 encoded data to binary data.
	@param	dp	Destination pointer.
	@param	ds	Destination size.
	@param	sp	Source pointer.
	@param	ss	Source size.
	@return	Number of binary bytes produced.
*/
static
size_t
do_ra85_to_bin DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  size_t back = 0;
  unsigned char *mydp; char *mysp;
  unsigned long v;
  short vused;
  size_t i;
  
  mydp = (unsigned char *)dp; mysp = sp; v = 0UL; vused = 0;
  for(i = 0; i < ss; i++) {
    if(is_a85(*mysp)) {
      v += f2[vused++] * ((((unsigned long)((unsigned char)(*mysp))) & last_byte) - 33UL);
      if(vused >= 5) {
        *(mydp++) = (unsigned char)(v         & last_byte);
	*(mydp++) = (unsigned char)((v >>  8) & last_byte);
	*(mydp++) = (unsigned char)((v >> 16) & last_byte);
	*(mydp++) = (unsigned char)((v >> 24) & last_byte);
	back += 4;
        v = 0UL; vused = 0;
      }
    }
    mysp++;
  }
  if(vused) {
    vused--;
    while(vused--) {
      *(mydp++) = (unsigned char)(v         & last_byte);
      back++;
      v = v >> 8;
    }
  } 
  return back;
}



size_t
dkenc_ra85_to_bin DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  size_t back = 0;
  size_t needed_size;
  
  if((dp) && (sp) && (ds) && (ss)) {
    needed_size = dkenc_size_a85_to_bin(ss);
    if(needed_size) {
      if(ds >= needed_size) {
        back = do_ra85_to_bin(dp, ds, sp, ss);
      }
    }
  } 
  return back;
}



size_t
dkenc_ra85string_to_bin DK_P3(char *,dp, size_t,ds, char *,sp) {
  size_t back = 0;
  
  if(sp) {
    back = dkenc_ra85_to_bin(dp, ds, sp, strlen(sp));
  }
  return back;
}



/**	Convert binary data to ASCII-85 string.
	@param	dp	Destination pointer.
	@param	ds	Destination size.
	@param	sp	Source pointer.
	@param	ss	Source size.
*/
static
void
do_bin_to_a85 DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  register unsigned char *mydp;
  register unsigned char *mysp;
  register unsigned long v;
  register short vused;
  register short addval;
  register size_t i;
  
  mydp = (unsigned char *)dp;
  mysp = (unsigned char *)sp;
  v = 0UL;
  vused = 0;
  for(i = 0; i < ss; i++) {
    switch(vused) {
      case 3: {
        v |= ( ((unsigned long)(*(mysp++)))        & 0x000000FFUL);
      } break;
      case 2: {
        v |= ((((unsigned long)(*(mysp++))) <<  8) & 0x0000FF00UL);
      } break;
      case 1: {
        v |= ((((unsigned long)(*(mysp++))) << 16) & 0x00FF0000UL);
      } break;
      default: {
        v |= ((((unsigned long)(*(mysp++))) << 24) & 0xFF000000UL);
      } break;
    }
    if(++vused >= 4) {
      vused = 5;
      while(vused--) {
        *(mydp++) = (unsigned char)(33UL + v / f2[vused]);
	v = v % f2[vused];
      }
      v = 0UL; vused = 0;
    }
  }
  if(vused) {
    vused++; addval = 5 - vused;
    while(vused--) {
      *(mydp++) = (unsigned char)(33UL + v / f2[vused + addval]);
      v = v % f2[vused + addval];
    }
  }
  *mydp = '\0'; 
}



int
dkenc_bin_to_a85 DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  int back = 0;
  size_t needed_size;
  
  if((dp) && (sp) && (ds) && (ss)) {
    needed_size = dkenc_size_bin_to_a85(ss);
    if(needed_size) {
      if(ds >= needed_size) {
        do_bin_to_a85(dp, ds, sp, ss);
	back = 1;
      }
    }
  } 
  return back;
}



/**	Convert ASCII-85 encoded data to binary data.
	@param	dp	Destination pointer.
	@param	ds	Destination size.
	@param	sp	Source pointer.
	@param	ss	Source size.
	@return	Number of binary bytes produced.
*/
static
size_t
do_a85_to_bin DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  register size_t back = 0;
  register unsigned char *mydp;
  register unsigned char *mysp;
  register unsigned long v;
  register short vused;
  register size_t i;
  unsigned long u1, u2, u3;
  
  mydp = (unsigned char *)dp; mysp = (unsigned char *)sp;
  v = 0UL; vused = 0;
  for(i = 0; i < ss; i++) {
    if(*mysp) {
      if(is_a85(*mysp)) {
        v += f2[4 - vused] *
	((((unsigned long)(*mysp)) & 0x000000FFUL) - 33UL);
      }
      vused++;
      if(vused >= 5) {
        *(mydp++) = (unsigned char)((v >> 24) & 0x000000FFUL); back++;
	*(mydp++) = (unsigned char)((v >> 16) & 0x000000FFUL); back++;
	*(mydp++) = (unsigned char)((v >>  8) & 0x000000FFUL); back++;
	*(mydp++) = (unsigned char)( v        & 0x000000FFUL); back++;
        v = 0UL; vused = 0;
      }
    }
    mysp++;
  }
  if(vused) {
    u1 = (v >> 24) & 0x000000FFUL;
    u2 = (v >> 16) & 0x000000FFUL;
    u3 = (v >>  8) & 0x000000FFUL;
    switch(vused) {
      case 2: {
        if(v & 0x00FFFFFFUL) {
	  u1++; if(u1 >= 256UL) u1 = 0UL;
	}
	*(mydp++) = (unsigned char)(u1 & 0x000000FFUL); back++;
      } break;
      case 3: {
        if(v & 0x0000FFFFUL) {
	  u2++;
	  if(u2 >= 256UL) {
	    u2 = 0UL;
	    u1++; if(u1 >= 256UL) u1 = 0UL;
	  }
	}
	*(mydp++) = (unsigned char)(u1 & 0x000000FFUL); back++;
	*(mydp++) = (unsigned char)(u2 & 0x000000FFUL); back++;
      } break;
      case 4: {
        if(v & 0x000000FFUL) {
	  u3++;
	  if(u3 >= 256UL) {
	    u3 = 0UL;
	    u2++;
	    if(u2 >= 256UL) {
	      u2 = 0UL;
	      u1++; if(u1 >= 256UL) u1 = 0UL;
	    }
	  }
	}
	*(mydp++) = (unsigned char)(u1 & 0x000000FFUL); back++;
	*(mydp++) = (unsigned char)(u2 & 0x000000FFUL); back++;
	*(mydp++) = (unsigned char)(u3 & 0x000000FFUL); back++;
      } break;
    }
  } 
  return back;
}



size_t
dkenc_a85_to_bin DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  size_t back = 0;
  size_t needed_size;
  
  if((dp) && (sp) && (ds) && (ss)) {
    needed_size = dkenc_size_a85_to_bin(ss);
    if(needed_size) {
      if(ds >= needed_size) {
        back = do_a85_to_bin(dp, ds, sp, ss);
      }
    }
  } 
  return back;
}



size_t
dkenc_a85string_to_bin DK_P3(char *,dp, size_t,ds, char *,sp) {
  size_t back = 0;
  
  if(sp) {
    back = dkenc_a85_to_bin(dp, ds, sp, strlen(sp));
  } 
  return back;
}



size_t
dkenc_size_bin_to_hex DK_P1(size_t,s) {
  size_t back = 0;
  int ec = 0;
  unsigned long ul1;
  
  ul1 = (unsigned long)s;
  ul1 = dkma_mul_ulong_ok(ul1, 2UL, &ec);
  ul1 = dkma_add_ulong_ok(ul1, 1UL, &ec);
  back = (size_t)ul1;
  if(ec) back = 0;
  if((unsigned long)back != ul1) back = 0;
  
  return back;
}



size_t
dkenc_size_hex_to_bin DK_P1(size_t,s) {
  size_t back;
  
  back = s / 2;
  back++; 
  return back;
}



/**	Digits used to show hexadecimal characters.
*/
static char hex_digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', '\0'
};



/**	Get higher half-byte from character.
	@param	c	Source character.
	@return	Higher half-byte of \a c.
*/
static
char
high_nibble_hex DK_P1(char,c) {
  char back;
  back = hex_digits[ (((unsigned short)c) >> 4) & 0x000FU ];
  return back;
}



/**	Get lower half-byte from character.
	@param	c	Source character.
	@return	Lower half-byte of \a c.
*/
static
char
low_nibble_hex DK_P1(char,c) {
  char back;
  back = hex_digits[  ((unsigned short)c)       & 0x000FU ];
  return back;
}



/**	Convert binary data to hexadecimal string.
	@param	dp	Destination pointer.
	@param	ds	Destination size.
	@param	sp	Source pointer.
	@param	ss	Source size.
*/
static
void
do_bin_to_hex DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  register char *mydp;
  register char *mysp;
  register size_t i;
  
  mydp = dp; mysp = sp;
  for(i = 0; i < ss; i++) {
    *(mydp++) = high_nibble_hex(*mysp);
    *(mydp++) = low_nibble_hex(*(mysp++));
  }
  *mydp = '\0'; 
}



int
dkenc_bin_to_hex DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  int back = 0;
  size_t needed_bytes;
  
  if((dp) && (ds) && (sp) && (ss)) {
    needed_bytes = dkenc_size_bin_to_hex(ss);
    if(needed_bytes) {
      if(ds >= needed_bytes) {
        do_bin_to_hex(dp, ds, sp, ss); back = 1;
      }
    }
  } 
  return back;
}



/**	Convert hexadecimal data to binary data.
	@param	dp	Destination pointer.
	@param	ds	Destination size.
	@param	sp	Source pointer.
	@param	ss	Source size.
	@return	Number of bytes created in destination.
*/
static
size_t
do_hex_to_bin DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  register size_t back = 0;
  register char *mysp;
  register unsigned char *mydp;
  register unsigned char v;
  register short int vused;
  register size_t i;
  
  mydp = (unsigned char *)dp;
  mysp = sp; v = 0x00; vused = 0U;
  for(i = 0; i < ss; i++) {
    switch(*mysp) {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case 'a':
      case 'A':
      case 'b':
      case 'B':
      case 'c':
      case 'C':
      case 'd':
      case 'D':
      case 'e':
      case 'E':
      case 'f':
      case 'F':
      {
        if(vused) {
	  switch(*mysp) {
	    case '0': { v |= 0x00; } break;
	    case '1': { v |= 0x01; } break;
	    case '2': { v |= 0x02; } break;
	    case '3': { v |= 0x03; } break;
	    case '4': { v |= 0x04; } break;
	    case '5': { v |= 0x05; } break;
	    case '6': { v |= 0x06; } break;
	    case '7': { v |= 0x07; } break;
	    case '8': { v |= 0x08; } break;
	    case '9': { v |= 0x09; } break;
	    case 'a': { v |= 0x0A; } break;
	    case 'A': { v |= 0x0A; } break;
	    case 'b': { v |= 0x0B; } break;
	    case 'B': { v |= 0x0B; } break;
	    case 'c': { v |= 0x0C; } break;
	    case 'C': { v |= 0x0C; } break;
	    case 'd': { v |= 0x0D; } break;
	    case 'D': { v |= 0x0D; } break;
	    case 'e': { v |= 0x0E; } break;
	    case 'E': { v |= 0x0E; } break;
	    case 'f': { v |= 0x0F; } break;
	    case 'F': { v |= 0x0F; } break;
	  }
	  *(mydp++) = v; back++; v = 0; vused = 0U;
	} else {
	  switch(*mysp) {
	    case '0': { v = 0x00; } break;
	    case '1': { v = 0x10; } break;
	    case '2': { v = 0x20; } break;
	    case '3': { v = 0x30; } break;
	    case '4': { v = 0x40; } break;
	    case '5': { v = 0x50; } break;
	    case '6': { v = 0x60; } break;
	    case '7': { v = 0x70; } break;
	    case '8': { v = 0x80; } break;
	    case '9': { v = 0x90; } break;
	    case 'a': { v = 0xA0; } break;
	    case 'A': { v = 0xA0; } break;
	    case 'b': { v = 0xB0; } break;
	    case 'B': { v = 0xB0; } break;
	    case 'c': { v = 0xC0; } break;
	    case 'C': { v = 0xC0; } break;
	    case 'd': { v = 0xD0; } break;
	    case 'D': { v = 0xD0; } break;
	    case 'e': { v = 0xE0; } break;
	    case 'E': { v = 0xE0; } break;
	    case 'f': { v = 0xF0; } break;
	    case 'F': { v = 0xF0; } break;
	  }
	  vused = 1U;
	}
      } break;
    }
    mysp++;
  }
  if(vused) {
    *mydp = v; back++;
  } 
  return back;
}



size_t
dkenc_hex_to_bin DK_P4(char *,dp, size_t,ds, char *,sp, size_t,ss) {
  size_t back = 0;
  size_t needed_bytes;
  
  if((dp) && (ds) && (sp) && (ss)) {
    needed_bytes = dkenc_size_hex_to_bin(ss);
    if(needed_bytes) {
      if(ds >= needed_bytes) {
        back = do_hex_to_bin(dp, ds, sp, ss);
      }
    }
  } 
  return back;
}



size_t
dkenc_hexstring_to_bin DK_P3(char *,dp, size_t,ds, char *,sp) {
  size_t back = 0;
  
  if(sp) {
    back = dkenc_hex_to_bin(dp, ds, sp, strlen(sp));
  } 
  return back;
}



size_t
dkenc_size_hexstring_to_bin DK_P1(char *,s) {
  size_t back = 0;
  
  if(s) {
    back = strlen(s);
    back = dkenc_size_hex_to_bin(back);
  } 
  return back;
}



size_t
dkenc_size_a85string_to_bin DK_P1(char *,s) {
  size_t back = 0;
  
  if(s) {
    back = strlen(s);
    back = dkenc_size_a85_to_bin(back);
  } 
  return back;
}




