/* Dumb printing routines
   
   Copyright (C) 1996 Pete A. Zaitcev
                 1997 Jakub Jelinek
		 2001 Ben Collins
   
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
   USA.  */

#include "promlib.h"
#include <stringops.h>

/*
 * This part is rewritten by Igor Timkin <ivt@msu.su>. Than I
 * rewritten it again but kept the MISS style, originated from
 * Wladimir Butenko. --P3
 */
#define OBSIZE      200

#ifndef NULL
#define NULL (void *)0
#endif

static void putchar_1 (char c)
{
    /* P3: This buffer can be static while xprintf flushes it on exit. */
    static char buff[OBSIZE + 1];
    static int ox;

    if ((buff[ox] = c) == 0) {
	prom_puts (buff, ox);
	ox = 0;
    } else {
	if (++ox >= OBSIZE) {
	    buff[ox] = 0;
	    prom_puts (buff, ox);
	    ox = 0;
	}
    }
}

int putchar (int __c)
{
    char c = (char) __c;
    if (c == '\n')
	putchar_1 ('\r');
    putchar_1 (c);

    return __c;
}

/*
 * Print an unsigned integer in base b, avoiding recursion.
 */
static int printn (long long n, int b)
{
    static char prbuf[33];
    register char *cp;
    int count = 0;

    if (b == 10 && n < 0) {
	putchar ('-');
	count++;
	n = -n;
    }
    cp = prbuf;
    do
	*cp++ = "0123456789ABCDEF"[(unsigned int) (((unsigned long)n) % b)];
    while ((n = ((unsigned long long)n) / b & 0x0FFFFFFFFFFFFFFFULL));
    do {
	putchar (*--cp);
	count++;
    } while (cp > prbuf);

    return count;
}

int vprintf (char *fmt, va_list adx)
{
    register int c;
    char *s;
    int count = 0;

    for (;;) {
	while ((c = *fmt++) != '%') {
	    if (c == '\0') {
		putchar (0);
		return count;
	    }
	    putchar (c);
	}
	c = *fmt++;
	if (c == 'd' || c == 'o' || c == 'x' || c == 'X') {
	    count += printn ((long long) va_arg (adx, unsigned),
			     c == 'o' ? 8 : (c == 'd' ? 10 : 16));
	} else if (c == 'c') {
	    putchar (va_arg (adx, unsigned));
	    count++;
	} else if (c == 's') {
	    if ((s = va_arg (adx, char *)) == NULL)
		s = (char *)"(null)";
	    while ((c = *s++)) {
		putchar (c);
		count++;
	    }
	} else if (c == 'l' || c == 'O') {
	    count += printn ((long long) va_arg (adx, long), c == 'l' ? 10 : 8);
	} else if (c == 'L') {
	    int hex = 0;
	    if (*fmt == 'x') {
		fmt++;
		hex = 1;
	    }
	    count += printn ((long long) va_arg (adx, long long), hex ? 16 : 10);
	} else {
	    /* This is basically what libc's printf does */
	    putchar('%'); putchar(c);
	    count += 2;
	}
    }

    return count;
}

/*
 * Scaled down version of C Library printf.
 * Only %c %s %d (==%u) %o %x %X %l %O are recognized.
 */

void prom_printf (char *fmt,...)
{
    va_list x1;

    va_start (x1, fmt);
    vprintf (fmt, x1);
    va_end (x1);
}

static int sprintn (char *str, long long n, int b)
{
    static char prbuf[33];
    register char *cp;
    int count = 0;

    if (b == 10 && n < 0) {
	memset (str + count, '-', 1);
	count++;
	n = -n;
    }
    cp = prbuf;
    do
	*cp++ = "0123456789ABCDEF"[(unsigned int) (((unsigned long)n) % b)];
    while ((n = ((unsigned long long)n) / b & 0x0FFFFFFFFFFFFFFFULL));
    do {
	memset (str + count, *--cp, 1);
	count++;
    } while (cp > prbuf);

    return count;
}

int vsprintf (char *str, char *fmt, va_list adx)
{
    register int c;
    char *s;
    int count = 0;

    for (;;) {
	while ((c = *fmt++) != '%') {
	    memset (str + count, c, 1);
	    if (c == '\0') {
		return count;
	    }
	}
	c = *fmt++;
	if (c == 'd' || c == 'o' || c == 'x' || c == 'X') {
	    count += sprintn (str + count, (long long) va_arg (adx, unsigned),
			     c == 'o' ? 8 : (c == 'd' ? 10 : 16));
	} else if (c == 'c') {
	    memset (str + count, va_arg (adx, unsigned), 1);
	    count++;
	} else if (c == 's') {
	    if ((s = va_arg (adx, char *)) == NULL)
		s = (char *)"(null)";
	    while ((c = *s++)) {
		memset (str + count, c, 1);
		count++;
	    }
	} else if (c == 'l' || c == 'O') {
	    count += sprintn (str + count, (long long) va_arg (adx, long), c == 'l' ? 10 : 8);
	} else if (c == 'L') {
	    int hex = 0;
	    if (*fmt == 'x') {
		fmt++;
		hex = 1;
	    }
	    count += sprintn (str + count, (long long) va_arg (adx, long long), hex ? 16 : 10);
	} else {
	    /* This is basically what libc's printf does */
	    memset (str + count, '%', 1);
	    count++;
	    memset (str + count, c, 1);
	    count++;
	}
    }

    return count;
}

/*
 * Scaled down version of C Library sprintf.
 * Only %c %s %d (==%u) %o %x %X %l %O are recognized.
 */

int sprintf (char *s, char *format, ...)
{
    va_list arg;
    int done;

    va_start (arg, format);
    done = vsprintf (s, format, arg);
    va_end (arg);

    return done;
}
