/****************************************************************/
/* Module name:   a64l.c                                        */
/* Library name:  mintlibs for Atari ST                         */
/* Author:        Hildo Biersma (boender@dutiws.twi.tudelft.nl) */
/* Date:          January 11, 1993                              */
/* Revision:      1 (first attempt)                             */
/*                2 ++entropy Made compatible with K&R compilers*/
/****************************************************************/

/* FIXME: maybe use ERANGE or EDOM instead of EBADARG */

/*
NAME
    a64l, l64a - convert between long integer and base-64 ASCII string

SYNOPSIS
    #include <support.h>
    long a64l(const char *s);
    char *l64a(long l);

DESCRIPTION
    These functions are used to maintain numbers stored in base-64
    ASCII characters. This is a notation by which long integers
    can be represented by up to six characters; each character
    represents a "digit" in a radix-64 notation.
    
    The characters used to represent "digits" are . for 0, / for 1,
    0 through 9 for 2-11, A through Z for 12-37, and a through z
    for 38-63.
    
    a64l takes a pointer to a null-terminated base-64 representation
    and returns a corresponding long value. If the string pointed to
    by s contains more than six characters, a64l will use the first
    six. a64l scans the character string from left to right, decoding
    each character as a 6 bit radix-64 number. If the string contains
    illegal characters, -1 is returned and errno is set to EBADARG.
    
    l64a takes a long argument and returns a pointer to the
    corresponding base-64 representation. If the argument is 0, a64l
    returns a pointer to a null string. If the argument is smaller
    than zero, a pointer to a null string is returned and errno is
    set to EBADARG.

CAVEATS
    The value returned by l64a is a pointer into a static buffer,
    the contents of which are overwritten by each call.
    
    The value returned by a64l may be incorrect if the value
    is too large; for that reason, only strings that resulted
    from a call to l64a should be used to call a64l.

    Maybe these calls should unsigned long values, but longs are
    used here to retain compatibility with UN*X System V.

AUTHOR
    Hildo Biersma, with the help of a UN*X System V man page.
*/

#include <errno.h>
extern int errno;

#ifndef _COMPILER_H
#include <compiler.h>
#endif

/* Local function prototypes */
static int a64i __PROTO((char c));  /* base-64 char to int, -1 on error */
static char i64a __PROTO((int i));  /* integer to base-64 char, 0x7F on error */

/* base-64 char to int, -1 on error */
static int a64i(c)
  char c;
{
  int retval = c;

  if ((c < '.') || (c > 'z'))
  {
    errno = EBADARG;
    return(-1);
  }
  retval -= '.';
  if (c > '9')
    retval -= 'A' - '9' - 1;
  if (c > 'Z')
    retval -= 'a' - 'Z' - 1;
  if (retval > 63)
  {
    errno = EBADARG;
    return(-1);
  }
  return(retval);
} /* End of a64i() */

/* base-64 string to long */
long a64l(s)
  const char *s;
{
  long retval = 0;
  int counter = 0;
  const char *ptr = s;

  while ((counter++ < 6) && (*ptr != 0x00))
  {
    int val;
    
    if ((val = a64i(*ptr++)) == -1)
      return(-1); /* errno was set by a64i() */
    retval <<= 6;
    retval += val;
  }
  return(retval);
} /* End of a64l() */

/* integer to base-64 char, 0x7F on error */
static char i64a(i)
  int i;
{
  char retval = (char)i;
  
  if ((i < 0) || (i > 63))
  {
    errno = EBADARG;
    return(0x7F);
  }
  retval += '.';
  if (i > 11)
    retval += 'A' - '9' - 1;
  if (i > 37)
    retval += 'a' - 'Z' - 1;
  return(retval);
} /* End of i64a() */

/* long to base-64 string */
char *l64a(l)
  long l;
{
  static char retval[7];
  char buffer[7], *ptr1 = buffer, *ptr2 = retval;
  int counter = 0;

  if (l < 0)
  {
    errno = EBADARG;
    return("");
  }
  if (l == 0)
    return("");
  while ((counter++ < 6) && (l > 0))
  {
    char val;
    
    if ((val = i64a((char)(l & 0x3F))) == 0x7F)
      return(""); /* errno was set by i64a() */
    *ptr1++ = val;
    l >>= 6;
  }
  while (ptr1 > buffer)
    *ptr2++ = *(--ptr1);
  *ptr2 = 0x00;
  return(retval);
} /* End of l64a() */
