/* -------------------------------------------------------------------- */
/*                                                                      */
/*                                                                      */
/* Copyright (C) 2014 portmaster - http://bsdforge.com                  */
/* Copyright (C) 2000 Angelo Masci                                      */
/*                                                                      */
/* All rights reserved.                                                 */
/*                                                                      */
/* --------------------------------------------------------------------
   $Id$
   -------------------------------------------------------------------- */

#include <stdarg.h>

#include "common.h"
#include "format.h"

/* -------------------------------------------------------------------- */

#define FALSE 0
#define TRUE (!FALSE)

#define NWIDTH 64	/* Current maximum width limit for numbers */

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int format(int (* copyfunc)(void *, char *, int), void (* flushfunc)(void *), void *cfarg, char *fmt, va_list args)
{
	int	res,
		written,
		n;

	char	*ptr,
		*optr,
		buf[NWIDTH];
	int	width;


	char	c;
	int 	signed_flag,
		long_flag;

	int	int_val;
	unsigned int
		uint_val;
	long	long_val;
	unsigned long
		ulong_val;


	written = 0;
	while (*fmt)
	{
		ptr = fmt;
		while ((*fmt) && (*fmt != '%'))
		{
			fmt++;
		}

		n = fmt - ptr;
		if (n)
		{
			res = (*copyfunc)(cfarg, ptr, n);
			if (res != n)
			{	goto finish;
			}

			written += res;
		}

		signed_flag = TRUE;
		long_flag = FALSE;

		if (*fmt == '%')
		{
			fmt++;

			width = 0;
			while ((*fmt >= '0') && (*fmt <= '9'))
			{
				width *= 10;
				width += (*fmt - '0');

				fmt++;
			}

			if (*fmt == 'l')
			{	long_flag = TRUE;
				fmt++;
			}

			switch (*fmt)
			{
				case 'c':
					c = (char)(va_arg(args, int));

					res = (*copyfunc)(cfarg, &c, 1);
					if (res != 1)
					{	goto finish;
					}

					written += res;
					break;

				case 's':
					ptr = (char *)(va_arg(args, int));

					optr = ptr;
					n    = 0;

					while(*ptr++)
					{	n++;
					}


					/* -------------------- */
					/* Output Left Padding 	*/

					if (width > n)
					{
						width -= n;
						while (width--)
						{
							res = (*copyfunc)(cfarg, " ", 1);
							if (res != 1)
							{	goto finish;
							}
						}

						written += res;
					}

					/* -------------------- */
					/* Output String 	*/

					res = (*copyfunc)(cfarg, optr, n);
					if (res != n)
					{	goto finish;
					}


					/* -------------------- */
					/* Output Right Padding */
					/* Unfinished ---	*/


					written += res;
					break;


				/* %[0-9]*d    int               */
				/* %[0-9]*u    unsigned int      */
				/* %[0-9]*ld   long int          */
				/* %[0-9]*lu   unsigned long int */

				case 'u':
					signed_flag = FALSE;
				case 'd':
					optr = &buf[NWIDTH];


					if (long_flag)
					{
						if (signed_flag)
						{	long_val = va_arg(args, long);

							do
							{	*--optr = '0' + (long_val % 10);
								long_val = long_val / 10;

							} while (long_val);
						}
						else
						{	ulong_val = (unsigned long)va_arg(args, long);

							do
							{	*--optr = '0' + (ulong_val % 10);
								ulong_val = ulong_val / 10;

							} while (ulong_val);
						}
					}
					else
					{
						if (signed_flag)
						{	int_val = va_arg(args, int);

							do
							{	*--optr = '0' + (int_val % 10);
								int_val = int_val / 10;

							} while (int_val);
						}
						else
						{	uint_val = (unsigned int)va_arg(args, int);

							do
							{	*--optr = '0' + (uint_val % 10);
								uint_val = uint_val / 10;

							} while (uint_val);
						}
					}

					/* -------------------- */
					/* Output Left Padding 	*/

					if (width > (&buf[NWIDTH] - optr))
					{
						width -= (&buf[NWIDTH] - optr);
						while (width--)
						{
							res = (*copyfunc)(cfarg, " ", 1);
							if (res != 1)
							{	goto finish;
							}
						}

						written += res;
					}


					/* -------------------- */
					/* Output Value 	*/

					res = (*copyfunc)(cfarg, optr, (&buf[NWIDTH] - optr));
					if (res != (&buf[NWIDTH] - optr))
					{	goto finish;
					}

					written += res;


					/* -------------------- */
					/* Output Right Padding */
					/* Unfinished ---	*/


					break;

				case '%':

					res = (*copyfunc)(cfarg, "%", 1);
					if (res != 1)
					{	goto finish;
					}

					written += res;
					break;

				default:
			}

			fmt++;
		}
	}

	res = (*copyfunc)(cfarg, "", 1);
	if (res != 1)
	{	goto finish;
	}


finish:
	written += res;

	if (flushfunc != NULL)
	{	(*flushfunc)(cfarg);
	}

	return written;
}

