/* vim: set filetype=c:
 *
 * cprintf.c86  -  printf() routine for target system
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  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
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: cprintf.c86,v 1.5 2007/01/06 18:30:51 gkminix Exp $
 */


/* Include target system C headers */
#include <common.h86>
#include <i386/stdarg.h86>
#include <i386/string.h86>
#include <i386/ctype.h86>
#include <i386/conio.h86>



/*
 * cprintf() is like printf() but it always prints to the console.
 */
size_t cprintf(const char *fmt, ...)
{
  char c;
  size_t count = 0;
  bool_t sign, upcase, dpoint;
  unsigned short type, base;
  short minsize, maxsize, len, i;
  long val;
  char *cp;
  char padch = ' ';
  va_list ap;

  va_start(ap, fmt);

  while ((c = *fmt++) != '\0') {
	/* If the character is not %, just print it */
	if (c != '%') {
		cputc(c);
		count++;
		continue;
	}

	/* Prepare for format handling */
	sign = FALSE;
	upcase = FALSE;
	dpoint = FALSE;
	type = 1;
	padch = *fmt;
	maxsize = minsize = 0;
	if (padch == '-' || padch == '0')
		fmt++;

	/* Determine the field sizes */
fmtnext:
	i = 0;
	for(;;) {
		c = *fmt++;
		if (c == '*') {
			i = (short)va_arg(ap, int);
			c = *fmt++;
			break;
		}
		if (!isdigit(c))
			break;
		i *= 10;
		i += c - '0';
	}

	if (c == '.' && !dpoint) {
		minsize = i;
		dpoint = TRUE;
		goto fmtnext;
	}
	if (dpoint)
		maxsize = i;
	else
		minsize = i;

	/* Determine the padding character and the argument size */
	if (padch == '-')
		minsize = -minsize;
	else if (padch != '0')
		padch = ' ';

	if (c == 'h') {
		c = *fmt++;
		type = 0;
	} else if (c == 'l') {
		c = *fmt++;
		type = 2;
	}
	if (c == '\0')
		break;

	/* Determine type of argument */
	switch(c) {
		case 'X': base = 16; type |= 4; sign = FALSE; upcase = TRUE;  goto printnum;
		case 'x': base = 16; type |= 4; sign = FALSE; upcase = FALSE; goto printnum;
		case 'o': base =  8; type |= 4; sign = FALSE; upcase = FALSE; goto printnum;
		case 'b': base =  2; type |= 4; sign = FALSE; upcase = FALSE; goto printnum;
		case 'u': base = 10; type |= 4; sign = FALSE; upcase = FALSE; goto printnum;
		case 'd': base = 10; type |= 0; sign = TRUE;  upcase = FALSE; goto printnum;
		case 's':
			cp = va_arg(ap, char *);
			len = _strlen(cp);
			goto printstr;
		case 'c':
			cputc((char)va_arg(ap, int));
			break;
		default:
			cputc(c);
			break;
	}
	continue;

	/* Print a numeric value */
printnum:
	switch(type) {
		case 0:  val = (short)va_arg(ap, int);  break; 
		case 1:  val = (int)  va_arg(ap, int);  break;
		case 2:  val = (long) va_arg(ap, long); break;
		case 4:  val = (unsigned short)va_arg(ap, unsigned int);  break; 
		case 5:  val = (unsigned int)  va_arg(ap, unsigned int);  break;
		case 6:  val = (unsigned long) va_arg(ap, unsigned long); break;
		default: val = 0; break;
	}
	if (sign) {
		sign = (val < 0);
		if (sign)
			val = -val;
	}
	cp = __numcvt(val, base);
	len = _strlen(cp) + (sign ? 1 : 0);

	/* Print a string value */
printstr:
	count--;
	if (!maxsize)
		maxsize = len;
	if (minsize > 0) {
		minsize -= len;
		while (minsize > 0) {
			cputc(padch);
			count++;
			minsize--;
		}
		minsize=0;
	}
	if (minsize < 0)
		minsize = -minsize - len;
	if (sign) {
		cputc('-');
		count++;
		maxsize--;
	}
	while (*cp && maxsize-- > 0) {
		/*
		 * We have to use the following strange construct because
		 * toupper() is implemented as a macro.
		 */
		c = *cp++;
		if (upcase)
			cputc(toupper(c));
		else
			cputc(c);
		count++;
	}
	while (minsize-- > 0) {
		cputc(' ');
		count++;
	}
  }

  va_end(ap);
  return(count);
}

