#include <string.h>
#include <stdarg.h>
#include <sys\types.h>

int		printk(const char *fmt,...);
int		sprintf(char * buf, const char *fmt, ...);
static int	vsprintf(char *, const char *, va_list);
static int	skip_atoi(const char **);
static char *	number(char *, long, int, int, int, int);

static void console_print(const char * buffer)
{
    __asm__ __volatile__(
	"movb   $0x09, %%ah \n\t"
	"int    $0x21 \n\t"
	: : "dx" ((int) buffer)
	);
}

int printk(const char *fmt, ...)
{
	char buf[5*80];
	char buf2[5*80];
	va_list args;
	int i,j;

	va_start(args, fmt);
	vsprintf(buf,fmt,args);
	va_end(args);

	for (i=0,j=0; buf[i]; i++,j++) {
	    buf2[j]=buf[i];
	    if (buf2[j]=='\n')
		buf2[++j]='\r';
	    }
	buf2[j]='$';

	console_print(buf2);
	return i;
}

int sprintf(char * buf, const char *fmt, ...)
{
	va_list args;
	int i;

	va_start(args, fmt);
	i=vsprintf(buf,fmt,args);
	va_end(args);
	return i;
}

static size_t
strlen(const char *str)
{
    register const char *s;

    for (s = str; *s; ++s);
    return (s - str);
}

/* we use this so that we can do without the ctype library */
#define is_digit(c)	((c) >= '0' && (c) <= '9')

static int
skip_atoi(const char **s)
{
    int 	    i = 0;

    while (is_digit(**s))
	i = i * 10 + *((*s)++) - '0';
    return i;
}

#define ZEROPAD 1		/* pad with zero */
#define SIGN	2		/* unsigned/signed long */
#define PLUS	4		/* show plus */
#define SPACE	8		/* space if plus */
#define LEFT	16		/* left justified */
#define SPECIAL 32		/* 0x */
#define SMALL	64		/* use 'abcdef' instead of 'ABCDEF' */

#ifdef __EMX__
#define do_div(n,base) ({	  \
int __res; \			  \
__asm__("divl %4"                 \
    :"=a" (n), "=d" (__res)       \
    :"0" (n),"1" (0),"r" (base)); \
__res; })
#else
#define do_div(n,base) ({ \
    unsigned long _res = (unsigned long) n % (unsigned long) base; \
    n = (unsigned long) n / (unsigned long) base; \
    (unsigned) _res; })
#endif

static char    *
number(char *str, long num, int base, int size, int precision, int type)
{
    char	    c, sign, tmp[36];
    const char	   *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int 	    i;

    if (type & SMALL)
	digits = "0123456789abcdefghijklmnopqrstuvwxyz";
    if (type & LEFT)
	type &= ~ZEROPAD;
    if (base < 2 || base > 36)
	return 0;
    c = (type & ZEROPAD) ? '0' : ' ';
    if (type & SIGN && num < 0) {
	sign = '-';
	num = -num;
    } else
	sign = (type & PLUS) ? '+' : ((type & SPACE) ? ' ' : 0);
    if (sign)
	size--;
    if (type & SPECIAL)
	if (base == 16)
	    size -= 2;
	else if (base == 8)
	    size--;
    i = 0;
    if (num == 0)
	tmp[i++] = '0';
    else
	while (num != 0) {
	    unsigned long _res = (unsigned long) num % (unsigned long) base;
	    num = (unsigned long) num / (unsigned long) base;
	    tmp[i++] = digits[_res];
	}
    if (i > precision)
	precision = i;
    size -= precision;
    if (!(type & (ZEROPAD + LEFT)))
	while (size-- > 0)
	    *str++ = ' ';
    if (sign)
	*str++ = sign;
    if (type & SPECIAL)
	if (base == 8)
	    *str++ = '0';
	else if (base == 16) {
	    *str++ = '0';
	    *str++ = digits[33];
	}
    if (!(type & LEFT))
	while (size-- > 0)
	    *str++ = c;
    while (i < precision--)
	*str++ = '0';
    while (i-- > 0)
	*str++ = tmp[i];
    while (size-- > 0)
	*str++ = ' ';
    return str;
}

static int
vsprintf(char *buf, const char *fmt, va_list args)
{
    int 	    len;
    int 	    i;
    char	   *str;
    char	   *s;
    int 	   *ip;

    int 	    flags;	/* flags to number() */

    int 	    field_width;/* width of output field */
    int 	    precision;	/* min. # of digits for integers; max number
				 * of chars for from string */
    int 	    qualifier;	/* 'h', 'l', or 'L' for integer fields */
    int 	    use_long = 0;

    for (str = buf; *fmt; ++fmt) {
	if (*fmt != '%') {
	    *str++ = *fmt;
	    continue;
	}
	/* process flags */
	flags = 0;
      repeat:
	++fmt;			/* this also skips first '%' */
	switch (*fmt) {
	case '-':
	    flags |= LEFT;
	    goto repeat;
	case '+':
	    flags |= PLUS;
	    goto repeat;
	case ' ':
	    flags |= SPACE;
	    goto repeat;
	case '#':
	    flags |= SPECIAL;
	    goto repeat;
	case '0':
	    flags |= ZEROPAD;
	    goto repeat;
	}

	/* get field width */
	field_width = -1;
	if (is_digit(*fmt))
	    field_width = skip_atoi(&fmt);
	else if (*fmt == '*') {
	    /* it's the next argument */
	    field_width = va_arg(args, int);
	    if (field_width < 0) {
		field_width = -field_width;
		flags |= LEFT;
	    }
	}
	/* get the precision */
	precision = -1;
	if (*fmt == '.') {
	    ++fmt;
	    if (is_digit(*fmt))
		precision = skip_atoi(&fmt);
	    else if (*fmt == '*') {
		/* it's the next argument */
		precision = va_arg(args, int);
	    }
	    if (precision < 0)
		precision = 0;
	}
	/* get the conversion qualifier */
	qualifier = -1;
	if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
	    if (*fmt != 'h')
		use_long = 1;
	    qualifier = *fmt;
	    ++fmt;
	}
	switch (*fmt) {
	case 'c':
	    if (!(flags & LEFT))
		while (--field_width > 0)
		    *str++ = ' ';
	    *str++ = (unsigned char) va_arg(args, int);
	    while (--field_width > 0)
		*str++ = ' ';
	    break;

	case 's':
	    s = va_arg(args, char *);
	    len = strlen(s);
	    if (precision < 0)
		precision = len;
	    else if (len > precision)
		len = precision;

	    if (!(flags & LEFT))
		while (len < field_width--)
		    *str++ = ' ';
	    for (i = 0; i < len; ++i)
		*str++ = *s++;
	    while (len < field_width--)
		*str++ = ' ';
	    break;

	case 'o':
	    str = number(str, va_arg(args, unsigned), 8,
			 field_width, precision, flags);
	    break;

	case 'p':
	    if (field_width == -1) {
		field_width = 8;
		flags |= ZEROPAD;
	    }
	    str = number(str, (unsigned) va_arg(args, void *), 16,
			 field_width, precision, flags);
	    break;

	case 'x':
	    flags |= SMALL;
	case 'X':
	    if (use_long)
		str = number(str, va_arg(args, unsigned long), 16,
			     field_width, precision, flags);
	    else
		str = number(str, va_arg(args, unsigned), 16,
			     field_width, precision, flags);
	    break;

	case 'd':
	case 'i':
	    flags |= SIGN;
	case 'u':
	    if (use_long)
		str = number(str, va_arg(args, unsigned), 10,
			     field_width, precision, flags);
	    else
		str = number(str, va_arg(args, unsigned long), 10,
			     field_width, precision, flags);
	    break;

	case 'n':
	    ip = va_arg(args, int *);
	    *ip = (str - buf);
	    break;

	default:
	    if (*fmt != '%')
		*str++ = '%';
	    if (*fmt)
		*str++ = *fmt;
	    else
		--fmt;
	    break;
	}
    }
    *str = '\0';
    return str - buf;
}
