/*
    UserSupport.c, Copyright 21.3.97 (c) by C. Conrad,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public Licence.
 
    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/
/*
	
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/User/UserSupport.c,v $
 	Author(s):             C. Conrad 
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 2936 $
 	Creation Date:         21.3.97
 	Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $      by: $Author: baderma $
	
*/

#include "UserSupport.h"
#include "Syscall.h"

void byteCopy( Address targetAddress,
               Address sourceAddress,
               unsigned long int nbBytes)
{
  char* src = (char*)sourceAddress;
  char* tar = (char*)targetAddress;
  unsigned long int i = 0;

  for (i = 0; i < nbBytes; i++)
  {
    *tar++ = *src++;
  }
}

void memcpy(char *a, char *b, int len)
{
  byteCopy(a, b, len);
}

void zeroOut(Address target, unsigned long int size)
{
  char* tar = (char*)target;
  unsigned long int i = 0;

  for (i = 0; i < size; i++)
    *tar++ = 0;
}

/* schneller als zeroOut */
void initmem(Address target, unsigned long int size, char c)
{
  char* tar = (char*)target;
  unsigned long int i = 0;

  for (i=size; i>0; i--)
    *tar++ = c;
}

void stringCopy( char* target,
                 char* source)
{
  while ((*target++ = *source++) != '\0')
    ;
}

void stringNCopy(char* target, char* source, unsigned long int size)
{
  while (((*target++ = *source++) != '\0') && ((--size) > 1))
    ;
  *target = '\0';
}


unsigned long int str_len(register char *s)
{
  register char *t;

  t = s;
  for (;;)
  {
    if (!*t)
      return t - s;
    ++t;
    if (!*t)
      return t - s;
    ++t;
    if (!*t)
      return t - s;
    ++t;
    if (!*t)
      return t - s;
    ++t;
  }
}


int stringLength( char* s)
{
  int len = 0;

  if (s!=NULL)
    while (*s++ != '\0')
      len++;

  return len;
}



void stringConcat(char *dest, char *source1, char *source2)
{
  char *ptr;
  ptr = dest;
  stringCopy(ptr, source1);
  ptr+=stringLength(source1);
  stringCopy(ptr,source2);
}

void int2string(char *str, int i)
{
  int j = 0,nrc = 0;

  j=i;
  nrc=0;
  while(j>0)
  {
    nrc++;
    j = (j - (j%10))/10;
  }
  if (i < 0)
  {
    nrc++;
    str[0] = '-';
  }
  if (i == 0)
    str[0] = '0';
  str[nrc+1] = 0;
  while(nrc >0)
  {
    str[--nrc] = (i%10) + 48;
    i = (i - (i%10))/10;
  }
}

void display( ThreadId tty, char* s)
/* s is a pointer to a '\0' terminated string, tty is the output device */
{
  char tmp[82], *t = tmp;
  unsigned long int i = 0;

#define LF2CR_TRANS 1

  while ( LF2CR_TRANS )
  {
    /* Translate newlines into carriage returns plus newlines for
    * correct terminal output
    */
    for (i=0; i < 82-2 /* '\0' and perhaps the last '\r' */ && *s; i++)
    {
      *t++ = *s;
      if ( *s++ == '\n')
      {
        *t++ = '\r';
        i++;
      }
    }

    *t='\0';
    t-=i;
    ioWrite(tty, t, &i);
    if ( ! *s )
      return;
  }
  i = (unsigned long int) stringLength(s);
  ioWrite(tty, s, &i );
}

int stringCompare(char* a, char* b)
{
  int i = 0;
  if (a != NULL && b != NULL)
  {
    while ((a[i] != '\0') && (b[i] != '\0'))
    {
      if (a[i] < b[i])
        return BEFORE;
      if (a[i] > b[i])
        return AFTER;
      i++;
    }
    /* a contains b or vice versa */
    if (a[i] == '\0' && b[i] == '\0')
      return EQUAL;
    if (a[i] == '\0')
      return BEFORE;
    return AFTER;
  }
  return UNDEFINED;
}

int stringNCompare(const char *s1, const char *s2, int n)
{

  if (n == 0 || s1 == NULL || s2 == NULL)
    return UNDEFINED;
  do
  {
    if (*s1 != *s2)
    {
      if (*s1 < *s2)
        return BEFORE;
      else
        return AFTER;
    }
    if (*s1 == 0 || *s2 == 0)
      break;
    s1++;
    s2++;
  }
  while (--n != 0 );
  if ((n == 0) || (*s1 == 0 && *s2 == 0))
    return EQUAL;
  if (*s1 == 0)
    return BEFORE;
  return AFTER;
}

int power(int base, int n)
{
  int p = 0;
  for (p=1;n>0;--n)
    p*=base;
  return p;
}


int atoi(int* intValue, char* string)
{
  char* currentPtr = string;
  int counter = 0;
  int nbChars = 1;

  *intValue=0;

  /* cur positioned on last byte not NULL */
  while (*(currentPtr+1) != '\0')
  {
    counter++;
    currentPtr++;
    nbChars++;
  }

  while (counter>=0)
  {
    if ( *currentPtr=='-')
    {
      if (counter==0)
      {
        *intValue *= (-1);
      }
      else
      {
        return -1;
      }
    }
    else
    {
      if ( *currentPtr-'0' < 0 || *currentPtr-'0' > 9)
      {
        return -1;
      }
      *intValue+=(*currentPtr-'0')*(power(10,nbChars-counter-1));
    }
    counter--;
    currentPtr--;
  }
  return 1;
}

void reverse(char s[])
{
  int c = 0,i = 0,j = 0;
  for(i=0,j=stringLength(s)-1;i<j;i++,j--)
  {
    c=s[i];
    s[i]=s[j];
    s[j]=c;
  }
}
void itoa(int n, char s[])
{
  int i = 0, sign = 0;

  if ((sign = n) < 0)
    n = -n;
  i=0;
  do
  {
    s[i++]=n%10+'0';
  }
  while ((n/=10)>0);
  if (sign<0)
    s[i++]='-';
  s[i]='\0';
  reverse(s);
}

/* The rest of the file is for printf and vsnprintf */

/* Macros for setting flags and vice versa */
#define SET_FLAG(f)		(flags |= f)
#define IS_FLAG(f)		(flags & f)
#define UNSET_FLAG(f)	(flags &= !f)

/* flags */
#define	ALT_FORM	0x01
#define L_ADJUST	0x02
#define ZERO_PAD	0x04
#define SIGNED		0x08
#define LONG_INT	0x11
#define SHORT_INT	0x12
#define VOID_PTR	0x14

#define SIGN_MASK   (1UL << (sizeof(long)*8-1))

#define FMT_LONG 44 /* enough space to hold 2^128 - 1 in decimal or octal plus \0 */

static void format_long(char *buf, unsigned long val, char type, int prec, char* sign, int flags)
{
  int base = 0;
  char tmp[FMT_LONG];
  int pos = 0, i = 0;
  char *digit = "0123456789ABCDEF";

  if( IS_FLAG(SIGNED) && (val & SIGN_MASK))
  {
    *buf++ = '-';
    val = ~val + 1; /* other vay to say val*=-1 :-) */
  }
  else if ( IS_FLAG(SIGNED) && sign )
  {
    *buf++ = *sign;
  }

  switch(type)
  {
  case 'd':
    base = 10;
    break;
  case 'o':
    base = 8;
    break;
  case 'x':
    digit = "0123456789abcdef";
  case 'X':
    base = 16;
    break;
  default:
    stringCopy(buf, "bug in vsprintf: bad base");
    return;
  }
  if(val==0)
  {
    tmp[0]=digit[0];
    pos++;
  }
  else
    while(val)
    {
      tmp[pos] = digit[val%base];
      val = val / base;
      pos++;
    }
  /* in tmp ist the unsigned number; pos is lenght */
  if(prec > 0 && pos < prec )
  {
    if ( prec >= FMT_LONG )
      prec = FMT_LONG-1; /* overflow protection */
    for(i=pos; i<prec; i++)
      *buf++ = '0'; /* pad with 0 if wanted precision is larger */
  }
  for(pos--; pos>=0; pos--)
  {
    *buf++ = tmp[pos];
  }
  *buf = '\0';
}


/* this is far from complete and standard...  */
/* kernel must not use floats anyway... (gfa) */
/* but acts like a real posix vsnprintf. (cj) */
int vsnprintf(char *buffer, unsigned long len, char *fmt, va_list ap)
{
  /* first some macros */
#define MWRITE(c) \
		if ( len ) { \
			*buffer++ = c; \
			len--; \
		} \
		written++ /* count even if buffer full (posix) */
  /* Macros for converting digits to letters and vice versa */
#define	TO_DIGIT(c)	((c) - '0')
#define IS_DIGIT(c)	((unsigned)TO_DIGIT(c) <= 9)
#define	TO_CHAR(n)	((n) + '0')
#define GET_WIDTH(ui, s) \
		ui = 0; \
		while ( IS_DIGIT(*s) ) { \
			ui = 10 * ui + TO_DIGIT(*s++); \
		} \
		--s /* while goes one to far */
  unsigned long written = 0, tlen;
  char *sign = 0;
  char numbuf[FMT_LONG];
  int flags = 0, done = 0, field = 0, pfield = 0, i = 0;
  char base; /* d for decimal, x for hex, X for HEX, o for octal */
  /* vars for va_arg */
  char c, *s;
  long l; /* shorts will be stored in longs */
  unsigned long ul;

  buffer[--len] = '\0'; /* buffer has to be 0-Terminated */
  while ( *fmt )
  {
    if (*fmt == '%')
    {
      done = 1;
      flags = 0;
      pfield = 0;
      field = 0;
      sign = 0;
      do
      {
        switch(*++fmt)
        {
        case '#':
          done = 0;
          SET_FLAG(ALT_FORM);
          break;
        case '-':
          done = 0;
          SET_FLAG(L_ADJUST);
          break;
        case '+':
          done = 0;
          sign = "+";
          break;
        case ' ':
          done = 0;
          if (! sign)
            sign = " ";
          break;
        case '0':
          done = 0;
          SET_FLAG(ZERO_PAD);
          break;
        case '.':
          done = 0;
          ++fmt; /* start with next char */
          GET_WIDTH(pfield, fmt);
          break;
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          done = 0;
          GET_WIDTH(field, fmt);
          break;
        case 'l':
          done = 0;
          SET_FLAG(LONG_INT);
          break;
        case 'h':
          done = 0;
          SET_FLAG(SHORT_INT);
          break;
        case 'd':
        case 'i':
          done = 1;
          base = 'd';
          SET_FLAG(SIGNED);
          goto number;
        case 'o':
          done = 1;
          base = 'o';
          goto number;
        case 'u':
          done = 1;
          base = 'd';
          goto number;
        case 'X':
          done = 1;
          base = 'X';
          goto number;
        case 'x':
          done = 1;
          base = 'x';
          goto number;
        case 'c':
          done = 1;
          c = (unsigned char)va_arg(ap, int);
          MWRITE(c);
          break;
        case 's':
          done = 1;
          s = va_arg(ap, char*);
          if ( s == 0 )
            s = "(null char)";
          if (! pfield > 0 )
          {
            pfield = stringLength(s);
          }
          i = field - pfield;
          if ( i > 0 && !IS_FLAG(L_ADJUST) )
          { /* right adjusted */
            while ( i-- )
            {
              if ( IS_FLAG(ZERO_PAD) )
              {
                MWRITE('0');
              }
              else
              {
                MWRITE(' ');
              }
            }
          }
          while( pfield-- )
          {
            if ( (c = *s++) == '\0' )
              break;
            MWRITE(c);
          }
          if ( i > 0 && IS_FLAG(L_ADJUST) )
          {
            while ( i-- )
            { /* left adjusted */
              MWRITE(' ');
            }
          }
          break;
        case 'p':
          done = 1;
          SET_FLAG(ALT_FORM|VOID_PTR);
          pfield = 8;
          base = 'x';
          goto number;
        case 'D':
          done = 1;
          SET_FLAG(LONG_INT|SIGNED);
          base = 'd';
          goto number;
        case 'O':
          done = 1;
          SET_FLAG(LONG_INT);
          base = 'o';
          goto number;
        case 'U':
          done = 1;
          SET_FLAG(LONG_INT);
          base = 'd';
number:
          /* this is the correct way to extract va_args */
          if ( IS_FLAG(SIGNED) )
          {
            l = IS_FLAG(LONG_INT) ? va_arg(ap, long) :
                IS_FLAG(SHORT_INT) ? (long)(short)va_arg(ap, int) :
                (long)va_arg(ap, int);
            format_long(numbuf, (unsigned long)l, base, pfield, sign, flags);
          }
          else
          {
            ul= IS_FLAG(LONG_INT) ? va_arg(ap, unsigned long) :
                IS_FLAG(SHORT_INT) ? (unsigned long)(unsigned short)va_arg(ap, int):
                IS_FLAG(VOID_PTR) ? (unsigned long)va_arg(ap, void*):
                (unsigned long)va_arg(ap, unsigned int);
            format_long(numbuf, ul, base, pfield, sign, flags);
          }
          /* now we have the padded, signed value in numbuf */
          /* now take care of field, alternate form, left adjust, zero pad */
          i = 0;
          if ( IS_FLAG(ALT_FORM) &&
               !( numbuf[0] == '0' && numbuf[1] == '\0' ) )
          {
            /* prepend only if != 0 */
            switch (base)
            {
            case 'x':
            case 'X':
              i = 2;
              break;
            case 'o':
              i = 1;
              break;
            }
          }
          tlen = stringLength(numbuf);
          pfield = field - tlen - i;
          if ( pfield > 0 && !IS_FLAG(L_ADJUST) )
          { /* right adjusted */
            while ( pfield-- )
            {
              if ( IS_FLAG(ZERO_PAD) )
              {
                MWRITE('0');
              }
              else
              {
                MWRITE(' ');
              }
            }
          }
          if ( i != 0 )
          { /* print alternate form */
            MWRITE('0');
            if ( i == 2 )
            {
              MWRITE(base);
            }
          }
          for ( i = 0; i < (int) tlen; i++ )
          {
            MWRITE(numbuf[i]);
          }
          if ( pfield > 0 && IS_FLAG(L_ADJUST) )
          {
            while ( pfield-- )
            { /* left adjusted only with spaces */
              MWRITE(' ');
            }
          }
          /* finaly all is written, move to next */
          break;
        default:
          done = 1;
          MWRITE(*fmt);
          if ( *fmt == '\0')
            return written-1; /* don't count '\0' */
          break;
        }
      }
      while ( !done );
      fmt++;
    }
    else
    {
      MWRITE(*fmt);
      fmt++;
    }
  }
  MWRITE('\0');
  return written-1; /* don't count '\0' */
}

int vprintf(ThreadId tty, char* fmt, va_list ap)
{
  unsigned long len;
  char buf[160];

  len = vsnprintf(buf, 160, fmt, ap);
  display(tty, buf);
  return len;
}

int printf(ThreadId tty, char* fmt, ...)
{
  va_list ap;
  unsigned long len;
  char buf[160];

  va_start(ap, fmt);
  /*	va_arg(ap, char*); *//* XXX ??? don't know why but it has to be here */
  /* at least on tardis and kom because there the va_arg is
  broken (ARRRGGGG) */
  len = vsnprintf(buf, 160, fmt, ap);
  display(tty, buf);
  va_end(ap);

  return len;
}

int snprintf(char *buf, unsigned long len, char *fmt, ...)
{
  va_list ap;
  int ret = 0;

  va_start(ap, fmt);
  ret = vsnprintf(buf, len, fmt, ap);
  va_end(ap);
  return ret;
}
