/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library 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
   Lesser General Public License for more details.

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

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <time.h>
#include <locale.h>
#include <wchar.h>
#include <limits.h>
#include <wchar.h>
#include "_locale.h"
#include "libp.h"

#ifdef _i386_
#define USE_FLOAT
#endif

#define NUM_SIZE 100

static char * getnum(char *string, LLONG_TYPE num,int radix,int lc,int unsign)
{
	int i,sz=0;
   unsigned LLONG_TYPE unum;
	for (i=0; i < NUM_SIZE-1; i++)
		string[i] = '0';
	string[NUM_SIZE-1] = 0;
	if (num < 0 && !unsign)
		unum = - num;
	else
		unum = num;
	i = NUM_SIZE-2;
	while (unum) {
   		string[i] = (char)((unum % radix)+ '0');
		if (string[i] > '9') {
			string[i] += 7;
			if (lc)
				string[i] += 32;
		}
		i--;
		unum /= radix;
		sz++;
	}
	if (sz == 0)
		return &string[NUM_SIZE - 2] ;
        
	return &string[i+1];
}
char *__onetostr(char *obuf,char **buffer, const char *restrict format, void *restrict arg,
        int *restrict count, int *restrict written)
{
   LLONG_TYPE c ;
   int ch ;
   char * sz;
   int fxs;
   int temp, signch;
	int *p;
    int lc = 0x20;
	int looping = 1;
	int issigned = 0, ljustify = 0, spaced = 0, prefixed = 0;
	int	leadzero = 0;
	int width = 0;
	int prec = -1;
	int mode = 0;
	int i;
	int isf=1, isg=0;
	long double fmant;
	int fsign,fexp;
	char	fbuf[64],*fbp,*xxx;
   int type ;
    NUMERIC_DATA *nd = __locale_data[LC_NUMERIC];
	char locbuf[NUM_SIZE],*ofm = format;
	while (looping) 
		switch (*format) {
			case '+':	issigned = 1;
					format++;
					break;
			case '-':	ljustify = 1;
					format++;
					break;
			case ' ': spaced = 1;
					format++;
					break;
			case '#': prefixed = 1;
					format++;
					break;
			case '0':	leadzero = 1;
					format++;
					break;
			default:
					looping = 0;
		}
    if (ljustify)
        leadzero = 0;
    if (issigned)
        spaced = 0;
	if (isdigit(*format)) {
		width = 0;
		while (isdigit(*format)) {
			width *= 10;
			width += *format++ - '0';
		}
	}
	else if (*format == '*') {
		width = *((int *)arg)++;
        if (width < 0) {
            width = -width;
            ljustify = 1;
        }
		(*count)++;
		format++;
	}
	if (*format == nd->decimal_point[0]) {
		format++;
        if (*format == '*') {
            prec = *((int *)arg)++;
            if (prec <0)
                prec=-1;
            (*count)++ ;
            format++;
        } else {
    		prec = 0;
	    	while (isdigit(*format)) {
		    	prec *= 10;
			    prec += *format++ - '0';
            }
		}
	}
   if (*format == 'h' || *format == 'l') {
		mode = *format++;
      if (mode !='h') {
        if (*format == 'l') {
            mode = '1' ;
            format++ ;
        }
      } else if (*format == 'h') {
        mode = 'c';
        format++;
      }
   }
    else if (*format == 'j') // intmax_t
        mode = '1', format++;
    else if (*format == 'L')
        mode = *format++;
    else if (*format == 'z' || *format == 't') /* size_t && ptrdiff_t */
        format++;
   type = *format++ ;
   if (! (type & 0x20)) {
     lc = 0;
     type |= 0x20;
   }
   switch (type) {
		case '%':
			*(*buffer)++ = '%';
			*(*buffer) = 0;
			break;
		case 'c':
            if (mode == 'l') {
                mbstate_t st;
                int l;
                memset(&st,0,sizeof(st));
                l = wcrtomb(fbuf,(wchar_t)*(wint_t *)arg,&st);
                (*count)++;
                if (width <l)
                    width = l;
                fbuf[l] = 0;
                memset(*buffer,' ',width);
                if (ljustify)
                    memcpy(*buffer,fbuf,l);
                else
                    memcpy(*buffer + width - l,fbuf,l);
                *buffer += width;
                **buffer = 0;
            } else {
                if (width < 1)
                    width = 1;
		    	(*count)++;
                ch = *(int *)arg;
                memset(*buffer,' ',width);
                if (ljustify)
                    *(*buffer) = ch;
                else
                    *(*buffer + width-1) = ch;
                *buffer += width;
		        *(*buffer) = 0;
            }
			break;
        case 'a':
            if (mode == 'L') {
                (*count)+=3 ;
				fmant = *(long double *)arg;
			}
			else {
				(*count)+=2;
				fmant = *(double *)arg;
			}
            c = *(unsigned long long *) &fmant;
            c <<= 1 ;
			fbp = fbuf;
            if (isnan(fmant))
                goto donan ;
            if (isinf(fmant))
                goto doinf;
			if (issigned || spaced || fsign < 0)
				if (fsign < 0)
					*fbp++='-';
				else if (spaced)
                    *fbp++ = ' ';
                else
					*fbp++='+';
            *fbp++ = '0' ;
            *fbp++ = 'X' + lc;
            *fbp++ = '1' ;
            if (prefixed || prec != 0)
                *fbp++ = nd->decimal_point[0] ;
            while (c && prec) {
                temp = (c >> 60) & 15;
                temp += '0';
                if (temp > '9')
                    temp += 7 + lc;
                *fbp++ = temp;
                c <<= 4 ;
                prec--;
            }
            while (prec > 0) {
                *fbp++ = '0';
                prec--;
            }
            // rounding)
            if (c < 0) {
                *fbp-- = 0;
                while (fbp != fbuf && *fbp != 'X') {
                    if (*fbp == nd->decimal_point[0])
                        fbp--;
                    (*fbp)++;
                    if (*fbp =='g' || *fbp == 'G')
                        *fbp = '0';
                    else {
                        if (*fbp == '9'+1)
                            *fbp = 'A' + lc;
                        break ;
                    }
                }
                fbp += strlen(fbp);
            }
            *fbp++ = 'P' + lc ;
            c = *(short *)(((char *)&fmant)+ 8);
            c &= 0x7fff;
            c -= 0x3fff;
            if (c < 0) {
                c = - c;
                *fbp++ = '-';
            } else
                *fbp++ = '+';
            sz = getnum(locbuf,c,10,0,1);
            strcpy(fbp,sz);
			i = strlen(fbuf);
            if (width < i)
                width = i;
                
            memset(*buffer,' ',width);
            (*buffer)[width] = 0;
			if (ljustify) {
				memcpy(*buffer,fbuf,i);
			}
			else {
                memcpy(*buffer + width - i,fbuf,i);
			}
			*buffer+=strlen(*buffer);	
            break ;            
		case 'g':
			isg = 1;
			goto dofloat;
		case 'e':
			isf = 0;
			goto dofloat;
		case 'f':
dofloat:
             if (mode == 'L') {
                (*count)+=3 ;
				fmant = *(long double *)arg;
			 }
			 else {
				(*count)+=2;
				fmant = *(double *)arg;
			 }
#ifndef USE_FLOAT
			strcpy(*buffer,"(FP not linked)");
			*buffer += strlen(*buffer);
#else
    		fbp = fbuf;
            if (isnan(fmant)) {
      
                float f ;
                int d ;
donan:               
                if (signbit(fmant))
                    *fbp++ = '-';
                else
                    *fbp++ = '+';
                if (lc)
    				strcpy(fbp,"nan");
                else
                    strcpy(fbp,"NAN");
                fbp += 3;
                f = fmant;
                d = *(int *)&f;
                d >>= 3 ;
                d &= 0xfffff ;
                sz = getnum(locbuf,d,16,lc,1);
                if (strlen(sz) < 5)
                    sz -= 5 - strlen(sz);
                strcpy(fbp+3,sz);
            } else if (isinf(fmant)) {
doinf:
                if (signbit(fmant))
                    *fbp++ = '-';
                else
                    *fbp++ = '+';
                if (lc)
    				strcpy(fbp,"inf");
                else
                    strcpy(fbp,"INF");
            } else {
                int bcdIndex = 0;
                unsigned char bcdBuf[100];
 			    __fextract(&fmant,&fexp,&fsign, bcdBuf) ;
				/* sign */
				if (issigned || spaced || fsign < 0)
					if (fsign < 0)
						*fbp++='-';
					else if (spaced)
                        *fbp++ = ' ';
                    else
						*fbp++='+';

                {
					if (fexp > sizeof(fbuf)-1 || fexp < - (sizeof(fbuf)-1))
					{
						isg = 1;
						if (prec < 1)
							prec = 6;
					}
					/* adjust for negative exponents, I was too lazy to do this in ASM */
					if (fexp < 0) {
                        bcdIndex++;
						fexp--;
					}


					/* decide on g format */
                    if (isg)
					{
                        isf = fexp <= -prec && fexp >= -5;
					}

                    if (isg) 
                    {
                        if (prec < 1)
                            prec = 6 ;
                    }
					else if (prec == -1)
						prec = 6;

					xxx = fbp;		/* pointer to the roundup digit */
					*fbp++ = '0';						/* in case of round from 9.99999999.... */

					/* mantissa */
					if (isf) {
                        int digits = 10000;
                        if (isg)
                            digits = prec ;
						if (fexp < 0) {
							*fbp++ = '0';
                            digits--;
                            if (prefixed || (!isg  || digits) && prec > 0)
      							*fbp++ = nd->decimal_point[0];
                            if (digits) {
                                if (prec > 0) {
									int use_fnd = 0;
    		    					if (fexp < -prec-1)
									{
	    		    					fexp = -prec-1;
									}
									else
										use_fnd = 1;
		    		    			for (i=fexp; i < -1 && digits; i++)
			    		    			*fbp++ = '0',digits--;
				    		    	for (i=-fexp-1; i < prec && digits; i++)
									{
					    		    	*fbp++ = __fnd(bcdBuf,bcdIndex++) + '0',digits--;
									}
									if (use_fnd)
										i = __fnd(bcdBuf,bcdIndex++);
									else
										i = 0;
                                }
                            }
						}
						else {
                            int digits = 10000;
                            if (isg)
                                digits = prec ;
							*fbp++ = __fnd(bcdBuf,bcdIndex++) + '0';
                            digits --;
							while (fexp > 0 && digits) {
								*fbp++ = __fnd(bcdBuf,bcdIndex++) + '0';
                                digits--;
								fexp--;
							}
                            if (!isg && prec > 0 || isg && digits || prefixed)
    							*fbp++ = nd->decimal_point[0];
							for (i=0; i < prec && digits; i++) {
								*fbp++ = __fnd(bcdBuf,bcdIndex++) + '0';
                                digits--;
                            }
							i = __fnd(bcdBuf,bcdIndex++);
						}
					}
					else {
                        int digits = 10000;
						int count = 30;
                        if (isg)
                            digits = prec;
						*fbp++ = __fnd(bcdBuf,bcdIndex++) + '0';
                        digits--;
						if (fmant != 0.0L)
							while (!isg && *(fbp-1) == '0' && count--)
							{
								fexp--;
								*(fbp-1) = __fnd(bcdBuf, bcdIndex++) + '0';
							}
                        if (!isg && prec > 0 || isg && digits || prefixed)
   							*fbp++ = nd->decimal_point[0];
						for (i=0; i < prec && digits; i++)
							*fbp++ = __fnd(bcdBuf,bcdIndex++) + '0',digits--;
						i = __fnd(bcdBuf,bcdIndex++);
					}
		
					/* Rounding */
					if (i >4) {
						char *fbp2=fbp-1;
						while (fbp2 >= fbuf) {
							if (*fbp2 < '0') {
								fbp2--;
								continue;
							}
							(*fbp2)++;
							if (*fbp2 != 0x3a)
								break;
							(*fbp2--)='0';
						}
					}

					/* check for round out of first digit */
					*fbp = 0;
					if (*xxx == '0') {
						strcpy(xxx,xxx+1);	/* nope, blit it out */
						fbp--;
					}
					else {
						if (!isf) {
							*(xxx+1) = nd->decimal_point[0];	/* yes, readjust decimal */
							*(xxx+2) = '0';
							if (!isg)
								fbp--;	/* fix prec */
							fexp++;	/* fix exponent */
						}
						
					}
					/* zerowipe */
                    *fbp = 0;
                    if (isg && strchr(fbuf,nd->decimal_point[0]))
    					while (*(fbp-1) == '0')
	    					fbp--;
    
					// get rid of decimal point in G mode, when it is last char
					if (isg)
					{
						if (fbp[-1] == '.')
							*(--fbp) = 0;
					}
					/* exponent */
					if (!isf) {
						*fbp++ = 'E' + lc;
						if (fexp < 0) {
							fexp=-fexp;
							*fbp++='-';
						}
						else
							*fbp++='+';
                        sz = getnum(locbuf,fexp,10,0,0);
                        if (strlen(sz) < 2)
                            sz -= 2 - strlen(sz);
                        strcpy(fbp,sz);
                        fbp += strlen(fbp);
					}
					*fbp++ = 0;
					/* if -0.000000..., wipe out the minus */
					if (fbuf[0] == '-')
					{
						char *fpx = fbuf + 1;
						while (*fpx && *fpx == '.' || *fpx == '0')
							fpx++;
						if (!*fpx)
							strcpy(fbuf, fbuf + 1);
					}
				}
			}
			i = strlen(fbuf);
            if (width < i)
                width = i;
                
            memset(*buffer,' ',width);
            (*buffer)[width] = 0;
			if (ljustify) {
				memcpy(*buffer,fbuf,i);
			}
			else {
                memcpy(*buffer + width - i,fbuf,i);
			}
			*buffer+=strlen(*buffer);	
#endif
			break;
		case 'n':
			(*count)++;
			p = *(int **)arg;
         if (mode == '1')
			*(LLONG_TYPE *)p = (int)(*written);
         else if (mode == 'l')
			*(long *)p = (int)(*written);
         else if (mode == 'h')
			*(short *)p = (int)(*written);
         else if (mode == 'c')
			*(char *)p = (int)(*written);
         else
			*(int *)p = (int)(*written);
			break;
		case 's':
            if (mode == 'l') {
                char *p;
                mbstate_t st;
                wchar_t *ss ;
                int l=0,next = 0, inc = -1;
                memset(&st,0,sizeof(st));
                (*count)++;
                ss = *(wchar_t **)arg;
                if (prec < 0)
                    prec = INT_MAX;
                while (l + next < prec) {
                    l += next ;
                    inc++;
                    if (!*ss)
                        break;
                    next = wcrtomb(fbuf,*ss++,&st);
                }
                if (width < l)
                    width = l;
                memset(*buffer,' ',width);
                memset(&st,0,sizeof(st));
                ss = *(wchar_t **)arg;
                if (ljustify)
                    p = *buffer;
                else
                    p = *buffer + width - l;
                for (i=0; i < inc; i++) {
                    p += wcrtomb(p,*ss++,&st);
                }
                *buffer += width;
                **buffer = 0;
            } else {
			    (*count)++;
                xxx = *(char **)arg ;
                if (xxx == NULL)
                    xxx = "(null)";
    			fxs = strlen(xxx);
    			if (prec > 0 && prec < fxs)
    				fxs = prec;
                if (width < fxs)
                    width = fxs ;
   				memset(*buffer,' ',width);
   				*((*buffer) + width) = 0;
                if (ljustify)
                    strncpy(*buffer,xxx,fxs);
                else
                    strncpy(*buffer + width - fxs,xxx,fxs);
    			*buffer += strlen(*buffer);
            }
			break;
        case 'p':
         leadzero = 1 ;
         prefixed = 1 ;
         type = 'x';
         if (!width)
            width = 8 ;
		case 'x':
		case 'u':
		case 'o':
        case 'd':
        case 'i':
            if (prec == -1)
                prec = 1;
			(*count)++;
         if (mode == '1') {
            c = *(LLONG_TYPE *)arg ;
            (*count)++ ;
         } else if (mode == 'l') {
            c = *(long *)arg;
            if (type != 'd' && type != 'i')
                c = (unsigned long)c;
         } else {
            c = *(int *)arg ;
            if (type != 'd' && type != 'i')
                c = (unsigned)c;
         }
         if (mode == 'h') {
            c = (short)c ;
            if (type != 'd' && type != 'i')
                c = (unsigned short)c;
         } else if (mode == 'c') {
            c = (char)c;
            if (type != 'd' && type != 'i')
                c = (unsigned char)c;
         }
			sz = getnum(locbuf,c,(type == 'o') ? 8 : (type == 'x') ? 16 : 10,
                        lc,type == 'x' || type == 'o' || type == 'u');
            if (prec == 0 && strlen(sz) == 1 && sz[0] == '0')
                sz++;
            else if (strlen(sz) < prec)
                sz -= prec - strlen(sz);
            signch = -1 ;
            if (type != 'x' && type != 'u' && type != 'o')
    			if (issigned) {
	    			if (c < 0) 
		    			signch = '-';
			    	else
				    	signch = '+';
    			}
	    		else if (spaced) {
		    		if (c < 0) 
			    		signch = '-';
				    else
					    signch = ' ';
			    }
			    else {
				    if (c < 0) {
					    signch = '-';
				    }
			    }
            temp = strlen(sz) ;
            if (signch != -1)
                temp++;
            if (prefixed)
                if (type == 'o' && sz[0] != '0')
                    temp++;
                else if ((type == 'x') && c)
                    temp += 2 ;
            if (temp > width)
                width = temp;
            if (leadzero)
                memset(*buffer,'0',width);
            else
                memset(*buffer,' ',width);
            (*buffer)[width] = 0;
            if (ljustify || leadzero)
                xxx = *buffer;
            else
                xxx = *buffer + width - temp;
            if (signch != -1)
                *xxx++ = signch;
            if (prefixed)
                if (type == 'o' && sz[0] != '0')
                   *xxx++ = '0';
                else if ((type == 'x') && c) {
                   *xxx++ = '0';
                   *xxx++ = 'X' + lc;
                }
            if (ljustify)
                memcpy(xxx,sz,strlen(sz));
            else
                memcpy(*buffer + width - strlen(sz),sz,strlen(sz));
            *buffer += strlen(*buffer);
			break;
		default:
		 	*(*buffer)++ = format[-1];
			break;
	}
	return(format);
}
