/*
** snprintf.c for  in 
** 
** Made by vianney rancurel
** Login   <vianney@epita.fr>
** 
** Started on  Wed Aug 25 12:09:57 1999 vianney rancurel
** Last update Thu Oct 28 20:15:21 1999 
*/
#include "config.h"

#ifndef HAVE_SNPRINTF

#include "mg.h"

/* REVISITED CODE */
/**************************************************************
 * Original:
 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
 * A bombproof version of doprnt (dopr) included.
 * Sigh.  This sort of thing is always nasty do deal with.  Note that
 * the version here does not include floating point...
 *
 * snprintf() is used instead of sprintf() as it does limit checks
 * for string length.  This covers a nasty loophole.
 *
 * The other functions are there to prevent NULL pointers from
 * causing nast effects.
 **************************************************************/

static char	*end = NULL;
static char	*output = NULL;

static void	dopr_outch(c)
int		c;
{
  if (iscntrl(c) && c != '\n' && c != '\t')
    {
      c = '@' + (c & 0x1f);
      if (end == 0 || output < end)
	{
	  *output++ = '^';
	}
    }
  if (end == 0 || output < end)
    {
      *output++ = c;
    }
}

static void	dostr(str)
char		*str;
{
  while (*str)
    dopr_outch(*str++);
}

static void	fmtstr(value,ljust,len,zpad)
char		*value;
int		ljust;
int		len;
int		zpad;
{
  int		padlen;
  int		strlen;

  if (value == NULL)
    value = "(null)";
  for (strlen = 0;value[strlen];++strlen);
  padlen = len - strlen;
  if (padlen < 0)
    padlen = 0;
  if (ljust) 
    padlen = -padlen;
  while (padlen > 0)
    {
      dopr_outch(' ');
      --padlen;
    }
  dostr(value);
  while (padlen < 0)
    {
      dopr_outch(' ');
      ++padlen;
    }
}

static void	fmtnum(value,base,dosign,ljust,len,zpad)
long		value;
int		base;
int		dosign;
int		ljust;
int		len;
int		zpad;
{
  int		signvalue;
  unsigned long	uvalue;
  char		convert[STR_BUFSIZ];
  int		place;
  int		padlen;
  int		caps;

  signvalue = place = padlen = caps = 0;
  uvalue = value;
  if (dosign)
    {
      if (value < 0)
	{
	  signvalue = '-';
	  uvalue = -value;
	}
    }
  if (base < 0)
    {
      caps = 1;
      base = -base;
    }
  do
    {
      convert[place++] = 
	(caps?"0123456789ABCDEF":"0123456789abcdef")[uvalue % (unsigned)base];
      uvalue = (uvalue / (unsigned)base);
    }
  while (uvalue);
  convert[place] = 0;
  padlen = len - place;
  if (padlen < 0)
    padlen = 0;
  if (ljust)
    padlen = -padlen;
  if (zpad && padlen > 0)
    {
      if (signvalue)
	{
	  dopr_outch(signvalue);
	  --padlen;
	  signvalue = 0;
	}
      while (padlen > 0)
	{
	  dopr_outch(zpad);
	  --padlen;
	}
    }
  while (padlen > 0)
    {
      dopr_outch(' ');
      --padlen;
    }
  if (signvalue)
    dopr_outch(signvalue);
  while (place > 0)
    dopr_outch(convert[--place]);
  while (padlen < 0)
    {
      dopr_outch(' ');
      ++padlen;
    }
}

#ifdef HAVE_STDARG_H
int		snprintf(char *str,int count,char *fmt,...)
#else
int		snprintf(str,count,fmt,va_alist)
char		*str;
int		count;
char		*fmt;
va_dcl
#endif
{
  va_list	ap;
  char		buf[BUFSIZ];
  
#ifdef HAVE_STDARG_H
  va_start(ap,fmt);
#else
  va_start(ap);
  va_arg(ap,char *);
  va_arg(ap,int); 
  va_arg(ap,char *);
#endif
  vsnprintf(str,count,fmt,ap);
  va_end(ap);
  return (strlen(str));
}

static VOID_FUNC	dopr(buffer,format,args)
char			*buffer;
char			*format;
va_list			args;
{
  int			ch;
  long			value;
  int			longflag;
  char			*strvalue;
  int			ljust;
  int			len;
  int			zpad;

  longflag = 0;
  output = buffer;
  while ((ch = *format++))
    {
      switch (ch)
	{
	case '%':
	  ljust = len = zpad = 0;
	nextch:
	  ch = *format++;
	  switch (ch)
	    {
	    case 0:
	      dostr("(end)");
	      return ;
	    case '-':
	      ljust = 1; 
	      goto nextch;
	    case '0': /* set zero padding if len not set */
	      if (len == 0)
		zpad = '0';
	    case '1': case '2': case '3':
	    case '4': case '5': case '6':
	    case '7': case '8': case '9':
	      len = len*10 + ch - '0';
	      goto nextch;
	    case 'l': 
	      longflag = 1;
	      goto nextch;
	    case 'u': case 'U':
	      if (longflag)
		value = va_arg(args,long);
	      else 
		value = va_arg(args,int);
	      fmtnum(value,10,0,ljust,len,zpad);
	      break;
	    case 'o': case 'O':
	      if (longflag)
		value = va_arg(args,long);
	      else
		value = va_arg(args,int);
	      fmtnum(value,8,0,ljust,len,zpad);
	      break;
	    case 'd': case 'D':
	      if (longflag)
		value = va_arg(args,long);
	      else
		value = va_arg(args,int);
	      fmtnum(value,10,1,ljust,len,zpad);
	      break;
	    case 'p':
	    case 'x':
	      if (longflag)
		value = va_arg(args,long);
	      else
		value = va_arg(args,int);
	      fmtnum(value,16,0,ljust,len,zpad);
	      break;
	    case 'X':
	      if (longflag)
		value = va_arg(args,long);
	      else
		value = va_arg(args,int);
	      fmtnum(value,-16,0,ljust,len,zpad);
	      break;
	    case 's':
	      strvalue = va_arg(args,char *);
	      fmtstr(strvalue,ljust,len,zpad);
	      break;
	    case 'c':
	      ch = va_arg(args,int);
	      dopr_outch(ch);
	      break;
	    case '%': 
	      dopr_outch(ch);
	      continue;
	    default:
	      dostr("(bad fmt)");
	    }
	  longflag = 0;
	  break;
	default:
	  dopr_outch(ch);
	  break;
	}
    }
  *output = 0;
}

int		vsnprintf(str,count,fmt,args)
char		*str;
int		count;
char		*fmt;
va_list		args;
{
  str[0] = 0;
  end = str + count - 1;
  dopr(str,fmt,args);
  if (count > 0)
    end[0] = 0;
  return (strlen(str));
}
#endif
