calisto-20000323/
calisto-20000323/lib/
calisto-20000323/lib/etc/
calisto-20000323/lib/players/
calisto-20000323/lib/text/
calisto-20000323/log/
/*
 Calisto (c) 1998-2000 Peter Howkins, Matthew Howkins, Simon Howkins

 $Id: msnprintf.c,v 1.1 2000/03/02 20:58:41 peter Exp $

 $Log: msnprintf.c,v $
 Revision 1.1  2000/03/02 20:58:41  peter
 Initial revision


 */
static char rcsid[] = "$Id: msnprintf.c,v 1.1 2000/03/02 20:58:41 peter Exp $";

#include <stdarg.h>
#include <stdlib.h>    /* for atoi() */
#include <ctype.h>

/* 
 * For the FLOATING POINT FORMAT :
 *  the challenge was finding a way to
 *  manipulate the Real numbers without having
 *  to resort to mathematical function (it
 *  would require to link with -lm) and not
 *  going down to the bit pattern (not portable)
 *
 *  so a number, a real is:

      real = integral + fraction

      integral = ... + a(2)*10^2 + a(1)*10^1 + a(0)*10^0
      fraction = b(1)*10^-1 + b(2)*10^-2 + ...

      where:
       0 <= a(i) => 9 
       0 <= b(i) => 9 
 
    from then it was simple math
 */

/*
 * size of the buffer for the integral part
 * and the fraction part 
 */
#define MAX_INT  99 + 1 /* 1 for the null */
#define MAX_FRACT 29 + 1

/* 
 * numtoa() uses PRIVATE buffers to store the results,
 * So this function is not reentrant
 */
#define itoa(n) (numtoa(n, 10, 0, (char **) 0))
#define otoa(n) (numtoa(n,  8, 0, (char **) 0))
#define htoa(n) (numtoa(n, 16, 0, (char **) 0))
#define dtoa(n, p, f) numtoa(n, 10, p, f)

#define SWAP_INT(a,b) {int t; t = (a); (a) = (b); (b) = t;}

/* this struct holds everything we need */
struct DATA {
  int length;
  char *holder;
  int counter;
  const char *pf;
/* FLAGS */
  int width, precision;
  int justify; char pad;
  int square, space, star_w, star_p, a_long ;
};

#define PUBLIC
/* signature of the functions */
/* the floating point stuff */
static double pow_10(int);
static int log_10(double);
static double integral(double, double *);
static char * numtoa(double, int, int, char **);

/* for the format */
static void conv_flag(const char *, struct DATA *);
static void floating(struct DATA *, double);
static void exponent(struct DATA *, double);
static void decimal(struct DATA *, double);
static void octal(struct DATA *, double);
static void hexa(struct DATA *, double);
static void strings(struct DATA *, char *);

/* those are defines specific to snprintf to hopefully
 * make the code clearer :-)
 */
#define RIGHT 1
#define LEFT  0
#define NOT_FOUND -1
#define FOUND 1
#define MAX_FIELD 15

/* the conversion flags */
#define isflag(c) ((c) == '#' || (c) == ' ' || \
                   (c) == '*' || (c) == '+' || \
                   (c) == '-' || (c) == '.' || \
                   isdigit(c))

/* round off to the precision */
#define ROUND(d, p) \
            (d < 0.) ? \
             d - pow_10(-(p)->precision) * 0.5 : \
             d + pow_10(-(p)->precision) * 0.5

/* set default precision */
#define DEF_PREC(p) \
            if ((p)->precision == NOT_FOUND) \
              (p)->precision = 6

/* put a char */
#define PUT_CHAR(c, p) \
            if ((p)->counter < (p)->length) { \
              *(p)->holder++ = (c); \
              (p)->counter++; \
            }

#define PUT_PLUS(d, p) \
            if ((d) > 0. && (p)->justify == RIGHT) \
              PUT_CHAR('+', p)

#define PUT_SPACE(d, p) \
            if ((p)->space == FOUND && (d) > 0.) \
              PUT_CHAR(' ', p)

/* pad right */ 
#define PAD_RIGHT(p) \
            if ((p)->width > 0 && (p)->justify != LEFT) \
              for (; (p)->width > 0; (p)->width--) \
                 PUT_CHAR((p)->pad, p)

/* pad left */
#define PAD_LEFT(p) \
            if ((p)->width > 0 && (p)->justify == LEFT) \
              for (; (p)->width > 0; (p)->width--) \
                 PUT_CHAR((p)->pad, p)

/* if width and prec. in the args */
#define STAR_ARGS(p) \
            if ((p)->star_w == FOUND) \
              (p)->width = va_arg(args, int); \
            if ((p)->star_p == FOUND) \
              (p)->precision = va_arg(args, int)

#include "msnprintf.h"

/*
 * Find the nth power of 10
 */
static double
pow_10(int n)
{ 
  int i;
  double P;

  if (n < 0)
    for (i = 1, P = 1.0, n = -n; i <= n; i++)
      P *= 0.1;
  else
    for (i = 1, P = 1.0; i <= n ; i++)
      P *= 10.0;
  return P;
}

/*
 * Find the integral part of the log in base 10 
 * Note: this not a real log10()
         I just need and approximation(integerpart) of x in:
          10^x ~= r
 * log_10(200) = 2;
 * log_10(250) = 2;
 */
static int
log_10(double r)
{ 
  int i = 0;
  double result = 1.0;

  if (r < 0.0)
    r = -r;

  if (r < 1.0) {
    while (result >= r) {result *= 0.1; i++;}
    return (-i);
  } else {
    while (result <= r) {result *= 10.0; i++;}
    return (i - 1);
  }
}

/*
 * This function return the fraction part of a double
 * and set in ip the integral part.
 * In many ways it resemble the modf() found on most Un*x
 */
static double
integral(double real, double * ip)
{ 
  int j;
  double i, s, p;
  double real_integral = 0.0;

/* take care of the obvious */
/* equal to zero ? */
  if (real == 0.0) {
    *ip = 0.0;
    return 0.0;
  }

/* negative number ? */
  if (real < 0.0)
    real = -real;

/* a fraction ? */
  if (real < 1.0) {
    *ip = 0.0;
    return real;
  }
  /* the real work :-) */
  for (j = log_10(real); j >= 0; j--) {
    p = pow_10(j);
    s = (real - real_integral) / p;
    i = 0.0;
    while (i + 1.0 <= s)
      i++;
    real_integral += i * p;
  }
  *ip = real_integral;
  return (real - real_integral);
}

#define PRECISION 1.e-6
/* 
 * return an ascii representation of the integral part of the number
 * and set fract to be an ascii representation of the fraction part
 * the container for the fraction and the integral part or staticly
 * declare with fix size 
 */
static char *
numtoa(double number, int base, int precision, char **fract)
{
  register int i, j;
  double ip, fp; /* integer and fraction part */
  double fraction;
  int digits = MAX_INT - 1;
  static char integral_part[MAX_INT];
  static char fraction_part[MAX_FRACT];
  double sign;
  int ch;

  /* taking care of the obvious case: 0.0 */
  if (number == 0.0) {
    integral_part[0] = '0';
    integral_part[1] = '\0';
    fraction_part[0] = '0';
    fraction_part[1] = '\0';
    return integral_part;
  }

  /* for negative numbers */
  if ((sign = number) < 0.0) {
    number = -number;
    digits--; /* sign consume one digit */
  } 

  fraction = integral(number, &ip);
  number = ip;
  /* do the integral part */
  if (ip == 0.0) {
    integral_part[0] = '0';
    i = 1;
  } else {
    for ( i = 0; i < digits && number != 0.0; ++i) {
      number /= base;
      fp = integral(number, &ip);
      ch = (int)((fp + PRECISION)*base); /* force to round */
      integral_part[i] = (ch <= 9) ? (ch + '0') : (ch + 'a' - 10);
      if (! isxdigit(integral_part[i])) /* bail out overflow !! */
        break; 
      number = ip;
     }
  }

  /* Oh No !! out of bound, ho well fill it up ! */
  if (number != 0.0)
    for (i = 0; i < digits; ++i)
      integral_part[i] = '9';

/* put the sign ? */
  if (sign < 0.0)
    integral_part[i++] = '-';

  integral_part[i] = '\0';

  /* reverse every thing */
  for ( i--, j = 0; j < i; j++, i--)
    SWAP_INT(integral_part[i], integral_part[j]);  

  /* the fractional part */
  for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision--) {
    fraction_part[i] = (int) ((fp + PRECISION) * 10.0 + '0');
    if (!isdigit(fraction_part[i])) /* underflow ? */
      break;
    fp = (fp * 10.0) - (double) (long) ((fp + PRECISION) * 10.0);
  }
  fraction_part[i] = '\0';

  if (fract != (char **) 0)
    *fract = fraction_part;

  return integral_part;

}

/* for %d and friends, it puts in holder
 * the representation with the right padding
 */
static void
decimal(struct DATA *p, double d)
{
  const char *tmp = itoa(d);

  p->width -= strlen(tmp);
  PAD_RIGHT(p);
  PUT_PLUS(d, p);
  PUT_SPACE(d, p);
  while (*tmp) { /* the integral */
    PUT_CHAR(*tmp, p);
    tmp++;
  }
  PAD_LEFT(p);
}

/* for %o octal representation */
static void
octal(struct DATA *p, double d)
{
  const char *tmp = otoa(d);

  p->width -= strlen(tmp);
  PAD_RIGHT(p);
  if (p->square == FOUND) /* had prefix '0' for octal */
    PUT_CHAR('0', p);
  while (*tmp) { /* octal */
    PUT_CHAR(*tmp, p);
    tmp++;
  }
  PAD_LEFT(p);
}

/* for %x %X hexadecimal representation */
static void
hexa(struct DATA *p, double d)
{
  const char *tmp = htoa(d);

  p->width -= strlen(tmp);
  PAD_RIGHT(p);
  if (p->square == FOUND) { /* prefix '0x' for hexa */
    PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
  }
  while (*tmp) { /* hexa */
    PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
    tmp++;
  }
  PAD_LEFT(p);
}

/* %s strings */
static void
strings(struct DATA *p, char *tmp)
{
  int i;

  i = strlen(tmp);
  if (p->precision != NOT_FOUND) /* the smallest number */
    i = (i < p->precision ? i : p->precision);
  p->width -= i;
  PAD_RIGHT(p);
  while (i-- > 0) { /* put the sting */
    PUT_CHAR(*tmp, p);
    tmp++;
  }
  PAD_LEFT(p);
}

/* %f or %g  floating point representation */
static void
floating(struct DATA *p, double d)
{
  char *tmp, *tmp2;
  int i;

  DEF_PREC(p);
  d = ROUND(d, p);
  tmp = dtoa(d, p->precision, &tmp2);
  /* calculate the padding. 1 for the dot */
  p->width = p->width -
            ((d > 0.0 && p->justify == RIGHT) ? 1 : 0) -
            ((p->space == FOUND) ? 1 : 0) -
            strlen(tmp) - p->precision - 1;
  PAD_RIGHT(p);  
  PUT_PLUS(d, p);
  PUT_SPACE(d, p);
  while (*tmp) { /* the integral */
    PUT_CHAR(*tmp, p);
    tmp++;
  }
  if (p->precision != 0 || p->square == FOUND)
    PUT_CHAR('.', p);  /* put the '.' */
  if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
    for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
       tmp2[i] = '\0'; 
  for (; *tmp2; tmp2++)
    PUT_CHAR(*tmp2, p); /* the fraction */
  
  PAD_LEFT(p);
} 

/* %e %E %g exponent representation */
static void
exponent(struct DATA *p, double d)
{
  char *tmp, *tmp2;
  int j, i;

  DEF_PREC(p);
  j = log_10(d);
  d = d / pow_10(j);  /* get the Mantissa */
  d = ROUND(d, p);                  
  tmp = dtoa(d, p->precision, &tmp2);
  /* 1 for unit, 1 for the '.', 1 for 'e|E',
   * 1 for '+|-', 2 for 'exp' */
  /* calculate how much padding need */
  p->width = p->width - 
             ((d > 0.0 && p->justify == RIGHT) ? 1 : 0) -
             ((p->space == FOUND) ? 1 : 0) - p->precision - 6;
  PAD_RIGHT(p);
  PUT_PLUS(d, p);
  PUT_SPACE(d, p);
  while (*tmp) {/* the integral */
    PUT_CHAR(*tmp, p);
    tmp++;
  }
  if (p->precision != 0 || p->square == FOUND)
    PUT_CHAR('.', p);  /* the '.' */
  if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
    for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
       tmp2[i] = '\0'; 
  for (; *tmp2; tmp2++)
    PUT_CHAR(*tmp2, p); /* the fraction */

  if (*p->pf == 'g' || *p->pf == 'e') { /* the exponent put the 'e|E' */
     PUT_CHAR('e', p);
   } else
     PUT_CHAR('E', p);
   if (j > 0) {  /* the sign of the exp */
     PUT_CHAR('+', p);
   } else {
     PUT_CHAR('-', p);
     j = -j;
   }
   tmp = itoa((double)j);
   if (j < 9) {  /* need to pad the exponent with 0 '000' */
     PUT_CHAR('0', p); /*PUT_CHAR('0', p);*/
   } /*else if (j < 99)
     PUT_CHAR('0', p);*/
   while (*tmp) { /* the exponent */
     PUT_CHAR(*tmp, p);
     tmp++;
   }
   PAD_LEFT(p);
}

/* initialize the conversion specifiers */
static void
conv_flag(const char *s, struct DATA *p)
{
  char number[MAX_FIELD/2];
  int i;

  p->precision = p->width   = NOT_FOUND;
  p->star_w    = p->star_p  = NOT_FOUND;
  p->square    = p->space   = NOT_FOUND;
  p->a_long    = p->justify = NOT_FOUND;
  p->pad = ' ';

  for ( ;s && *s ;s++) {
    switch (*s) {
      case ' ': p->space = FOUND; break;
      case '#': p->square = FOUND; break;
      case '*': if (p->width == NOT_FOUND)
                  p->width = p->star_w = FOUND;
                else
                  p->precision = p->star_p = FOUND;
                break;
      case '+': p->justify = RIGHT; break;
      case '-': p->justify = LEFT; break;
      case '.': if (p->width == NOT_FOUND)
                  p->width = 0;
                break;
      case '0': p->pad = '0'; break;
      case '1': case '2': case '3':
      case '4': case '5': case '6':
      case '7': case '8': case '9':     /* gob all the digits */
        for (i = 0; isdigit(*s); i++, s++) 
          if (i < MAX_FIELD/2 - 1)
            number[i] = *s;
        number[i] = '\0';
        if (p->width == NOT_FOUND)
          p->width = atoi(number);
        else
          p->precision = atoi(number);
        s--;   /* went to far go back */
        break;
    }
  }
}

int
mvsnprintf(char *string, size_t length, const char *format, va_list args)
{
  struct DATA data;
  char conv_field[MAX_FIELD];
  double d; /* temporary holder */
  int state;
  int i;

  data.length = length - 1; /* leave room for '\0' */
  data.holder = string;
  data.pf = format;
  data.counter = 0;

  /* sanity check, the string must be > 1 */
  if (length < 1)
    return -1;

  for (; *data.pf && (data.counter < data.length); data.pf++) {
    if ( *data.pf == '%' ) { /* we got a magic % cookie */
      conv_flag((char *)0, &data); /* initialise format flags */
      for (state = 1; *data.pf && state;) {
        switch (*(++data.pf)) {
          case '\0': /* a NULL here ? ? bail out */
            *data.holder = '\0';
            return data.counter;
            break;
          case 'f':  /* float, double */
            STAR_ARGS(&data);
            d = va_arg(args, double);
            floating(&data, d);  
            state = 0;
            break;
          case 'g': 
          case 'G':
            STAR_ARGS(&data);
            DEF_PREC(&data);
            d = va_arg(args, double);
            i = log_10(d);
            /*
             * for '%g|%G' ANSI: use f if exponent
             * is in the range or [-4,p] exclusively
             * else use %e|%E
             */
            if (i > -4 && i < data.precision)
              floating(&data, d);
            else
              exponent(&data, d);
            state = 0;
            break;
          case 'e':
          case 'E':  /* Exponent double */
            STAR_ARGS(&data);
            d = va_arg(args, double);
            exponent(&data, d);
            state = 0;
            break;
          case 'u':  /* unsigned decimal */
            STAR_ARGS(&data);
            if (data.a_long == FOUND)
              d = (double) va_arg(args, unsigned long);
            else
              d = (double) va_arg(args, unsigned);
            decimal(&data, d);
            state = 0;
            break;
          case 'i':
          case 'd':  /* decimal */
            STAR_ARGS(&data);
            if (data.a_long == FOUND)
              d = va_arg(args, long);
            else
              d = va_arg(args, int);
            decimal(&data, d);
            state = 0;
            break;
          case 'o':  /* octal */
            STAR_ARGS(&data);
            if (data.a_long == FOUND)
              d = va_arg(args, long);
            else
              d = va_arg(args, int);
            octal(&data, d);
            state = 0;
            break;
          case 'x': 
          case 'X':  /* hexadecimal */
            STAR_ARGS(&data);
            if (data.a_long == FOUND)
              d = va_arg(args, long);
            else
              d = va_arg(args, int);
            hexa(&data, d);
            state = 0;
            break;
          case 'c': /* character */
            d = va_arg(args, int);
            PUT_CHAR(d, &data);
            state = 0;
            break;
          case 's':  /* string */
            STAR_ARGS(&data);
            strings(&data, va_arg(args, char *));
            state = 0;
            break;
          case 'n':
             *(va_arg(args, int *)) = data.counter; /* what's the count ? */
             state = 0;
             break;
          case 'l':
            data.a_long = FOUND;
            break;
          case 'h':
            break;
          case '%':  /* nothing just % */
            PUT_CHAR('%', &data);
            state = 0;
            break;
          case '#': case ' ': case '+': case '*':
          case '-': case '.': case '0': case '1': 
          case '2': case '3': case '4': case '5':
          case '6': case '7': case '8': case '9':
           /* initialize width and precision */
            for (i = 0; isflag(*data.pf); i++, data.pf++) 
              if (i < MAX_FIELD - 1)
                conv_field[i] = *data.pf;
            conv_field[i] = '\0';
            conv_flag(conv_field, &data);
            data.pf--;   /* went to far go back */
            break;
          default:
            /* is this an error ? maybe bail out */
            state = 0;
            break;
        } /* end switch */
      } /* end of for state */
    } else { /* not % */
      PUT_CHAR(*data.pf, &data);  /* add the char the string */
    }
  }

  *data.holder = '\0'; /* the end ye ! */

  return data.counter;
}

int
msnprintf(char *string, size_t length, const char *format, ...)
{
  int rval;
  va_list args;

  va_start(args, format);
  rval = mvsnprintf(string, length, format, args);
  va_end(args);

  return rval;
}