/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1985-2012 AT&T Intellectual Property          *
*          Copyright (c) 2020-2021 Contributors to ksh 93u+m           *
*                      and is licensed under the                       *
*                 Eclipse Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*          http://www.eclipse.org/org/documents/epl-v10.html           *
*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                  David Korn <dgk@research.att.com>                   *
*                   Phong Vo <kpv@research.att.com>                    *
*                                                                      *
***********************************************************************/
#include	"sfhdr.h"
#include	"FEATURE/float"

/*	Dealing with $ argument addressing stuffs.
**
**	Written by Kiem-Phong Vo.
*/

static char* sffmtint(const char* str, int* v)
{	
	for(*v = 0; isdigit(*str); ++str)
		*v = *v * 10 + (*str - '0');
	*v -= 1;
	return (char*)str;
}

/* type>0: scanf, type==0: printf, type==-1: internal */
static Fmtpos_t* sffmtpos(Sfio_t* f,const char* form,va_list args,Sffmt_t* ft,int type)
{
	int		base, fmt, flags, dot, width, precis;
	ssize_t		n_str, size = 0;
	char		*t_str, *sp;
	int		v, n, skip, dollar, decimal, thousand;
	Sffmt_t		savft;
	Fmtpos_t*	fp;	/* position array of arguments	*/
	int		argp, argn, maxp, need[FP_INDEX];
	SFMBDCL(fmbs)

	if(type < 0)
		fp = NIL(Fmtpos_t*);
	else if(!(fp = sffmtpos(f,form,args,ft,-1)) )
		return NIL(Fmtpos_t*);

	dollar = decimal = thousand = 0; argn = maxp = -1;
	SFMBCLR(&fmbs);
	while((n = *form) )
	{	if(n != '%') /* collect the non-pattern chars */
		{	sp = (char*)form;
			for(;;)
			{	form += SFMBLEN(form, &fmbs);
				if(*form == 0 || *form == '%')
					break;
			}
			continue;
		}
		else	form += 1;
		if(*form == 0)
			break;
		else if(*form == '%')
		{	form += 1;
			continue;
		}

		if(*form == '*' && type > 0) /* skip in scanning */
		{	skip = 1;
			form += 1;
			argp = -1;
		}
		else /* get the position of this argument */
		{	skip = 0;
			sp = sffmtint(form,&argp);
			if(*sp == '$')
			{	dollar = 1;
				form = sp+1;
			}
			else	argp = -1;
		}

		flags = dot = 0;
		t_str = NIL(char*); n_str = 0;
		size = width = precis = base = -1;
		for(n = 0; n < FP_INDEX; ++n)
			need[n] = -1;

	loop_flags:	/* LOOP FOR \0, %, FLAGS, WIDTH, PRECISION, BASE, TYPE */
		switch((fmt = *form++) )
		{
		case LEFTP : /* get the type enclosed in balanced parens */
			t_str = (char*)form;
			for(v = 1;;)
			{	switch(*form++)
				{
				case 0 :	/* not balanceable, retract */
					form = t_str;
					t_str = NIL(char*);
					n_str = 0;
					goto loop_flags;
				case LEFTP :	/* increasing nested level */
					v += 1;
					continue;
				case RIGHTP :	/* decreasing nested level */
					if((v -= 1) != 0)
						continue;
					n_str = form-t_str;
					if(*t_str == '*')
					{	t_str = sffmtint(t_str+1,&n);
						if(*t_str == '$')
							dollar = 1;
						else	n = -1;
						if((n = FP_SET(n,argn)) > maxp)
							maxp = n;
						if(fp && fp[n].ft.fmt == 0)
						{	fp[n].ft.fmt = LEFTP;
							fp[n].ft.form = (char*)form;
						}
						need[FP_STR] = n;
					}
					goto loop_flags;
				}
			}

		case '-' :
			flags |= SFFMT_LEFT;
			flags &= ~SFFMT_ZERO;
			goto loop_flags;
		case '0' :
			if(!(flags&SFFMT_LEFT) )
				flags |= SFFMT_ZERO;
			goto loop_flags;
		case ' ' :
			if(!(flags&SFFMT_SIGN) )
				flags |= SFFMT_BLANK;
			goto loop_flags;
		case '+' :
			flags |= SFFMT_SIGN;
			flags &= ~SFFMT_BLANK;
			goto loop_flags;
		case '#' :
			flags |= SFFMT_ALTER;
			goto loop_flags;
		case QUOTE:
			SFSETLOCALE(&decimal,&thousand);
			if(thousand > 0)
				flags |= SFFMT_THOUSAND;
			goto loop_flags;

		case '.' :
			if((dot += 1) == 2)
				base = 0; /* for %s,%c */
			if(isdigit(*form))
			{	fmt = *form++;
				goto dot_size;
			}
			else if(*form != '*')
				goto loop_flags;
			else	form += 1;
			/* FALLTHROUGH */

		case '*' :
			form = sffmtint(form,&n);
			if(*form == '$' )
			{	dollar = 1;
				form += 1;
			}
			else	n = -1;
			if((n = FP_SET(n,argn)) > maxp)
				maxp = n;
			if(fp && fp[n].ft.fmt == 0)
			{	fp[n].ft.fmt = '.';
				fp[n].ft.size = dot;
				fp[n].ft.form = (char*)form;
			}
			if(dot <= 2)
				need[dot] = n;
			goto loop_flags;

		case '1' : case '2' : case '3' :
		case '4' : case '5' : case '6' :
		case '7' : case '8' : case '9' :
		dot_size :
			for(v = fmt - '0', fmt = *form; isdigit(fmt); fmt = *++form)
				v = v*10 + (fmt - '0');
			if(dot == 0)
				width = v;
			else if(dot == 1)
				precis = v;
			else if(dot == 2)
				base = v;
			goto loop_flags;

		case 'I' : /* object length */
			size = -1; flags = (flags & ~SFFMT_TYPES) | SFFMT_IFLAG;
			if(isdigit(*form) )
			{	for(size = 0, n = *form; isdigit(n); n = *++form)
					size = size*10 + (n - '0');
			}
			else if(*form == '*')
			{	form = sffmtint(form+1,&n);
				if(*form == '$' )
				{	dollar = 1;
					form += 1;
				}
				else	n = -1;
				if((n = FP_SET(n,argn)) > maxp)
					maxp = n;
				if(fp && fp[n].ft.fmt == 0)
				{	fp[n].ft.fmt = 'I';
					fp[n].ft.size = sizeof(int);
					fp[n].ft.form = (char*)form;
				}
				need[FP_SIZE] = n;
			}
			goto loop_flags;

		case 'l' :
			size = -1; flags &= ~SFFMT_TYPES;
			if(*form == 'l')
			{	form += 1;
				flags |= SFFMT_LLONG;
			}
			else	flags |= SFFMT_LONG;
			goto loop_flags;
		case 'h' :
			size = -1; flags &= ~SFFMT_TYPES;
			if(*form == 'h')
			{	form += 1;
				flags |= SFFMT_SSHORT;
			}
			else	flags |= SFFMT_SHORT;
			goto loop_flags;
		case 'L' :
			size = -1; flags = (flags & ~SFFMT_TYPES) | SFFMT_LDOUBLE;
			goto loop_flags;
		}

		/* set object size for scalars */
		if(flags & SFFMT_TYPES)
		{	if((_Sftype[fmt]&(SFFMT_INT|SFFMT_UINT)) || fmt == 'n')
			{	if(flags&SFFMT_LONG)
					size = sizeof(long);
				else if(flags&SFFMT_SHORT)
					size = sizeof(short);
				else if(flags&SFFMT_SSHORT)
					size = sizeof(char);
				else if(flags&SFFMT_TFLAG)
					size = sizeof(ptrdiff_t);
				else if(flags&SFFMT_ZFLAG) 
					size = sizeof(size_t);
				else if(flags&(SFFMT_LLONG|SFFMT_JFLAG) )
					size = sizeof(Sflong_t);
				else if(flags&SFFMT_IFLAG)
				{	if(size <= 0 ||
					   size == sizeof(Sflong_t)*CHAR_BIT )
						size = sizeof(Sflong_t);
				}
				else if(size < 0)
					size = sizeof(int);
			}
			else if(_Sftype[fmt]&SFFMT_FLOAT)
			{	if(flags&(SFFMT_LONG|SFFMT_LLONG))
					size = sizeof(double);
				else if(flags&SFFMT_LDOUBLE)
					size = sizeof(Sfdouble_t);
				else if(flags&SFFMT_IFLAG)
				{	if(size <= 0)
						size = sizeof(Sfdouble_t);
				}
				else if(size < 0)
					size = sizeof(float);
			}
			else if(_Sftype[fmt]&SFFMT_CHAR)
			{
#if _has_multibyte
				if((flags&SFFMT_LONG) || fmt == 'C')
				{	size = sizeof(wchar_t) > sizeof(int) ?
						sizeof(wchar_t) : sizeof(int);
				} else
#endif
				if(size < 0)
					size = sizeof(int);
			}
		}

		if(skip)
			continue;

		if((argp = FP_SET(argp,argn)) > maxp)
			maxp = argp;

		if(dollar && fmt == '!')
			return NIL(Fmtpos_t*);

		if(fp && fp[argp].ft.fmt == 0)
		{	fp[argp].ft.form = (char*)form;
			fp[argp].ft.fmt = fp[argp].fmt = fmt;
			fp[argp].ft.size = size;
			fp[argp].ft.flags = flags;
			fp[argp].ft.width = width;
			fp[argp].ft.precis = precis;
			fp[argp].ft.base = base;
			fp[argp].ft.t_str = t_str;
			fp[argp].ft.n_str = n_str;
			for(n = 0; n < FP_INDEX; ++n)
				fp[argp].need[n] = need[n];
		}
	}

	if(!fp) /* constructing position array only */
	{	if(!dollar || !(fp = (Fmtpos_t*)malloc((maxp+1)*sizeof(Fmtpos_t))) )
			return NIL(Fmtpos_t*);
		for(n = 0; n <= maxp; ++n)
			fp[n].ft.fmt = 0;
		return fp;
	}

	/* get value for positions */
	if(ft)
		memcpy(&savft, ft, sizeof(*ft));
	for(n = 0; n <= maxp; ++n)
	{	if(fp[n].ft.fmt == 0) /* gap: pretend it's a 'd' pattern */
		{	fp[n].ft.fmt = 'd';
			fp[n].ft.width = 0;
			fp[n].ft.precis = 0;
			fp[n].ft.base = 0;
			fp[n].ft.size = 0;
			fp[n].ft.t_str = 0;
			fp[n].ft.n_str = 0;
			fp[n].ft.flags = 0;
			for(v = 0; v < FP_INDEX; ++v)
				fp[n].need[v] = -1;
		}

		if(ft && ft->extf)
		{	fp[n].ft.version = ft->version;
			fp[n].ft.extf = ft->extf;
			fp[n].ft.eventf = ft->eventf;
			if((v = fp[n].need[FP_WIDTH]) >= 0 && v < n)
				fp[n].ft.width = fp[v].argv.i;
			if((v = fp[n].need[FP_PRECIS]) >= 0 && v < n)
				fp[n].ft.precis = fp[v].argv.i;
			if((v = fp[n].need[FP_BASE]) >= 0 && v < n)
				fp[n].ft.base = fp[v].argv.i;
			if((v = fp[n].need[FP_STR]) >= 0 && v < n)
				fp[n].ft.t_str = fp[v].argv.s;
			if((v = fp[n].need[FP_SIZE]) >= 0 && v < n)
				fp[n].ft.size = fp[v].argv.i;

			memcpy(ft,&fp[n].ft,sizeof(Sffmt_t));
			va_copy(ft->args,args);
			ft->flags |= SFFMT_ARGPOS;
			v = (*ft->extf)(f, (void*)(&fp[n].argv), ft);
			va_copy(args,ft->args);
			memcpy(&fp[n].ft,ft,sizeof(Sffmt_t));
			if(v < 0)
			{	memcpy(ft,&savft,sizeof(Sffmt_t));
				ft = NIL(Sffmt_t*);
			}

			if(!(fp[n].ft.flags&SFFMT_VALUE) )
				goto arg_list;
			else if(_Sftype[fp[n].ft.fmt]&(SFFMT_INT|SFFMT_UINT) )
			{	if(fp[n].ft.size == sizeof(short))
				{	if(_Sftype[fp[n].ft.fmt]&SFFMT_INT)
						fp[n].argv.i = fp[n].argv.h;
					else	fp[n].argv.i = fp[n].argv.uh;
				}
				else if(fp[n].ft.size == sizeof(char))
				{	if(_Sftype[fp[n].ft.fmt]&SFFMT_INT)
						fp[n].argv.i = fp[n].argv.c;
					else	fp[n].argv.i = fp[n].argv.uc;
				}
			}
			else if(_Sftype[fp[n].ft.fmt]&SFFMT_FLOAT )
			{	if(fp[n].ft.size == sizeof(float) )
					fp[n].argv.d = fp[n].argv.f;
			}
		}
		else
		{ arg_list:
			if(fp[n].ft.fmt == LEFTP)
			{	fp[n].argv.s = va_arg(args, char*);
				fp[n].ft.size = strlen(fp[n].argv.s);
			}
			else if(fp[n].ft.fmt == '.' || fp[n].ft.fmt == 'I')
				fp[n].argv.i = va_arg(args, int);
			else if(fp[n].ft.fmt == '!')
			{	if(ft)
					memcpy(ft,&savft,sizeof(Sffmt_t));
				fp[n].argv.ft = ft = va_arg(args, Sffmt_t*);
				if(ft->form)
					ft = NIL(Sffmt_t*);
				if(ft)
					memcpy(&savft,ft,sizeof(Sffmt_t));
			}
			else if(type > 0) /* from sfvscanf */
				fp[n].argv.vp = va_arg(args, void*);
			else switch(_Sftype[fp[n].ft.fmt])
			{ case SFFMT_INT:
			  case SFFMT_UINT:
#if !_ast_intmax_long
				if(size == sizeof(Sflong_t) )
					fp[n].argv.ll = va_arg(args, Sflong_t);
				else
#endif
				if(size == sizeof(long) )
					fp[n].argv.l = va_arg(args, long);
				else	fp[n].argv.i = va_arg(args, int);
				break;
			  case SFFMT_FLOAT:
#if !_ast_fltmax_double
				if(size == sizeof(Sfdouble_t) )
					fp[n].argv.ld = va_arg(args,Sfdouble_t);
				else
#endif
					fp[n].argv.d  = va_arg(args,double);
				break;
	 		  case SFFMT_POINTER:
					fp[n].argv.vp = va_arg(args,void*);
				break;
			  case SFFMT_CHAR:
				if(fp[n].ft.base >= 0)
					fp[n].argv.s = va_arg(args,char*);
#if _has_multibyte
				else if((fp[n].ft.flags & SFFMT_LONG) ||
					fp[n].ft.fmt == 'C' )
				{	if(sizeof(wchar_t) <= sizeof(int) )
					     fp[n].argv.wc = (wchar_t)va_arg(args,int);
					else fp[n].argv.wc = va_arg(args,wchar_t);
				}
#endif
					/* observe promotion rule */
				else	fp[n].argv.i = va_arg(args,int);
				break;
			  default: /* unknown pattern */
				break;
			}
		}
	}

	if(ft)
		memcpy(ft,&savft,sizeof(Sffmt_t));
	return fp;
}

static const unsigned char	flt_nan[] = { _ast_flt_nan_init };
static const unsigned char	flt_inf[] = { _ast_flt_inf_init };
static const unsigned char	dbl_nan[] = { _ast_dbl_nan_init };
static const unsigned char	dbl_inf[] = { _ast_dbl_inf_init };
#ifdef _ast_ldbl_nan_init
static const unsigned char	ldbl_nan[] = { _ast_ldbl_nan_init };
static const unsigned char	ldbl_inf[] = { _ast_ldbl_inf_init };
#endif

/* function to initialize conversion tables */
static int sfcvinit()
{	reg int		d, l;

	for(d = 0; d <= SF_MAXCHAR; ++d)
	{	_Sfcv36[d] = SF_RADIX;
		_Sfcv64[d] = SF_RADIX;
	}

	/* [0-9] */
	for(d = 0; d < 10; ++d)
	{	_Sfcv36[(uchar)_Sfdigits[d]] = d;
		_Sfcv64[(uchar)_Sfdigits[d]] = d;
	}

	/* [a-z] */
	for(; d < 36; ++d)
	{	_Sfcv36[(uchar)_Sfdigits[d]] = d;
		_Sfcv64[(uchar)_Sfdigits[d]] = d;
	}

	/* [A-Z] */
	for(l = 10; d < 62; ++l, ++d)
	{	_Sfcv36[(uchar)_Sfdigits[d]] = l;
		_Sfcv64[(uchar)_Sfdigits[d]] = d;
	}

	/* remaining digits */
	for(; d < SF_RADIX; ++d)
	{	_Sfcv36[(uchar)_Sfdigits[d]] = d;
		_Sfcv64[(uchar)_Sfdigits[d]] = d;
	}

	_Sftype['d'] = _Sftype['i'] = SFFMT_INT;
	_Sftype['u'] = _Sftype['o'] = _Sftype['x'] = _Sftype['X'] = SFFMT_UINT;
	_Sftype['e'] = _Sftype['E'] = _Sftype['a'] = _Sftype['A'] =
	_Sftype['g'] = _Sftype['G'] = _Sftype['f'] = SFFMT_FLOAT;
	_Sftype['s'] = _Sftype['n'] = _Sftype['p'] = _Sftype['!'] = SFFMT_POINTER;
	_Sftype['c'] = SFFMT_CHAR;
	_Sftype['['] = SFFMT_CLASS;
#if _has_multibyte
	_Sftype['S'] = SFFMT_POINTER;
	_Sftype['C'] = SFFMT_CHAR;
#endif

	/* IEEE floating point computed constants */

	memcpy((char*)&_Sffnan, (char*)flt_nan, sizeof(_Sffnan));
	memcpy((char*)&_Sffinf, (char*)flt_inf, sizeof(_Sffinf));
	memcpy((char*)&_Sfdnan, (char*)dbl_nan, sizeof(_Sfdnan));
	memcpy((char*)&_Sfdinf, (char*)dbl_inf, sizeof(_Sfdinf));
#ifdef _ast_ldbl_nan_init
	memcpy((char*)&_Sflnan, (char*)ldbl_nan, sizeof(_Sflnan));
	memcpy((char*)&_Sflinf, (char*)ldbl_inf, sizeof(_Sflinf));
#else
	memcpy((char*)&_Sflnan, (char*)dbl_nan, sizeof(_Sfdnan));
	memcpy((char*)&_Sflinf, (char*)dbl_inf, sizeof(_Sfdinf));
#endif

	return 1;
}

/* table for floating point and integer conversions */
#include	"FEATURE/sfinit"
