/*
 * Copyright (c) 2012 ARM Ltd
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the company may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <newlib.h>

#include <_ansi.h>
#include <reent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <wchar.h>
#include <sys/lock.h>
#include <stdarg.h>
#include "local.h"
#include "../stdlib/local.h"
#include "fvwrite.h"
#include "vfieeefp.h"

#include "vfprintf_local.h"

/* Decode and print non-floating point data. */
int
_printf_common (struct _reent *data,
		struct _prt_data_t *pdata,
		int *realsz,
		FILE *fp,
		int (*pfunc)(struct _reent *, int, FILE *))
{
	int n;		/* handy integer (short term usage) */
	/*
	 * All reasonable formats wind up here.  At this point, `cp'
	 * points to a string which (if not flags&LADJUST) should be
	 * padded out to `width' places.  If flags&ZEROPAD, it should
	 * first be prefixed by any sign or other prefix; otherwise,
	 * it should be blank padded before the prefix is emitted.
	 * After any left-hand padding and prefixing, emit zeroes
	 * required by a decimal [diouxX] precision, then print the
	 * string proper, then emit zeroes required by any leftover
	 * floating precision; finally, if LADJUST, pad with blanks.
	 * If flags&FPT, ch must be in [aAeEfg].
	 *
	 * Compute actual size, so we know how much to pad.
	 * size excludes decimal prec; realsz includes it.
	 */
	*realsz = pdata->dprec > pdata->size ? pdata->dprec : pdata->size;
	if (pdata->l_buf[0])
		(*realsz)++;
	if (pdata->flags & HEXPREFIX)
		*realsz += 2;

	/* right-adjusting blank padding */
	if ((pdata->flags & (LADJUST|ZEROPAD)) == 0)
		PAD (pdata->width - *realsz, pdata->blank);

	/* prefix */
	n = 0;
	if (pdata->l_buf[0])
		n++;

	if (pdata->flags & HEXPREFIX) {
		pdata->l_buf[n++] = '0';
		pdata->l_buf[n++] = pdata->l_buf[2];
	}

	PRINT (pdata->l_buf, n);
	n = pdata->width - *realsz;
	if ((pdata->flags & (LADJUST|ZEROPAD)) != ZEROPAD || n < 0)
		n = 0;
    
	if (pdata->dprec > pdata->size)
		n += pdata->dprec - pdata->size;
	PAD (n, pdata->zero);

	return 0;
error:
	return -1;
}
int
_printf_i (struct _reent *data, struct _prt_data_t *pdata, FILE *fp, 
	   int (*pfunc)(struct _reent *, int, FILE *), va_list *ap)
{
	int realsz;		/* field size expanded by dprec */
	u_quad_t _uquad;
	int base;
	int n;
	char *cp = pdata->buf + BUF;
	char *xdigs = "0123456789ABCDEF";

	/* decoding the conversion specifier. */
	switch (pdata->code) {
	case 'c':
		*--cp = GET_ARG (N, *ap, int);
		pdata->size = 1;
		goto non_number_nosign;
	case 'd':
	case 'i':
		_uquad = SARG (pdata->flags);
		if ((long) _uquad < 0) {
			_uquad = -_uquad;
			pdata->l_buf[0] = '-';
		}
		base = 10;
		goto number;
	case 'u':
	case 'o':
		_uquad = UARG (pdata->flags);
		base = (pdata->code == 'o') ? 8 : 10;
		goto nosign;
	case 'X':
		pdata->l_buf[2] = 'X';
		goto hex;
        case 'p':
		/*
		 * ``The argument shall be a pointer to void.  The
		 * value of the pointer is converted to a sequence
		 * of printable characters, in an implementation-
		 * defined manner.''
		 *	-- ANSI X3J11
		 */
		/* NOSTRICT */
		pdata->flags |= HEXPREFIX;
	case 'x':
		pdata->l_buf[2] = 'x';
		xdigs = "0123456789abcdef";
hex:
		_uquad = UARG (pdata->flags);
		base = 16;
		if (pdata->flags & ALT)
			pdata->flags |= HEXPREFIX;
		/* leading 0x/X only if non-zero */
		if (_uquad == 0)
			pdata->flags &= ~HEXPREFIX;

		/* unsigned conversions */
nosign:
		pdata->l_buf[0] = '\0';
		/*
		 * ``... diouXx conversions ... if a precision is
		 * specified, the 0 flag will be ignored.''
		 *	-- ANSI X3J11
		 */
number:
		if ((pdata->dprec = pdata->prec) >= 0)
			pdata->flags &= ~ZEROPAD;
        	/*
		 * ``The result of converting a zero value with an
		 * explicit precision of zero is no characters.''
		 *	-- ANSI X3J11
		 */
		if (_uquad != 0 || pdata->prec != 0) {
			do {
				*--cp = xdigs[_uquad % base];
				_uquad /= base;
			} while (_uquad);
		}
		pdata->size = pdata->buf + BUF - cp;
		/* For 'o' conversion, '#' increases the precision to force the first 
		 * digit of the result to be zero.  */
		if (base == 8 && (pdata->flags & ALT) && pdata->prec <= pdata->size)
			pdata->prec = pdata->size + 1;
		break;
	case 'n':
		if (pdata->flags & LONGINT)
			*GET_ARG (N, *ap, long_ptr_t) = pdata->ret;
		else if (pdata->flags & SHORTINT)
			*GET_ARG (N, *ap, short_ptr_t) = pdata->ret;
		else
			*GET_ARG (N, *ap, int_ptr_t) = pdata->ret;
        case '\0':
		pdata->size = 0;
		break;
	case 's':
		cp = GET_ARG (N, *ap, char_ptr_t);
		/* Precision gives the maximum number of chars to be written from a string,
		 * and take prec == -1 into consideration */
		if ((u_int)(pdata->size = strlen (cp)) > (u_int)(pdata->prec))
			pdata->size = pdata->prec;
		/* ??? Following check should be redundant.
		 * Note pdata->prec will be set to pdata->size if it is -1 previously. */
#if 0
		if (pdata->prec > pdata->size)
#endif
			pdata->prec = pdata->size;
		goto non_number_nosign;
	default:	/* "%?" prints ?, unless ? is NUL */
		/* pretend it was %c with argument ch */
		*--cp = pdata->code;
		pdata->size = 1;
non_number_nosign:
		pdata->l_buf[0] = '\0';
		break;
	}

	/***** output. *****/
	n = _printf_common (data, pdata, &realsz, fp, pfunc);
	if (n == -1)
		goto error;

	PRINT (cp, pdata->size);
	/* left-adjusting padding (always blank) */
	if (pdata->flags & LADJUST)
		PAD (pdata->width - realsz, pdata->blank);

	return (pdata->width > realsz ? pdata->width : realsz);
error:
	return -1;
}

