
/***************************************************************************
                                                                           *
Copyright 2013 CertiVox UK Ltd.                                           *
                                                                           *
This file is part of CertiVox MIRACL Crypto SDK.                           *
                                                                           *
The CertiVox MIRACL Crypto SDK provides developers with an                 *
extensive and efficient set of cryptographic functions.                    *
For further information about its features and functionalities please      *
refer to http://www.certivox.com                                           *
                                                                           *
* The CertiVox MIRACL Crypto SDK is free software: you can                 *
  redistribute it and/or modify it under the terms of the                  *
  GNU Affero General Public License as published by the                    *
  Free Software Foundation, either version 3 of the License,               *
  or (at your option) any later version.                                   *
                                                                           *
* The CertiVox MIRACL Crypto SDK 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 Affero General Public License for more details.              *
                                                                           *
* You should have received a copy of the GNU Affero General Public         *
  License along with CertiVox MIRACL Crypto SDK.                           *
  If not, see <http://www.gnu.org/licenses/>.                              *
                                                                           *
You can be released from the requirements of the license by purchasing     *
a commercial license. Buying such a license is mandatory as soon as you    *
develop commercial activities involving the CertiVox MIRACL Crypto SDK     *
without disclosing the source code of your own applications, or shipping   *
the CertiVox MIRACL Crypto SDK with a closed source product.               *
                                                                           *
***************************************************************************/
/*
 *   MIRACL I/O routines 1. 
 *   mrio1.c
 */

#include "miracl.h"

#ifndef MR_SIMPLE_IO

int instr(_MIPD_ flash x,char *string)
{  /*  input a big number       *
    *  returns length in digits */
    int i,ipt,n,s,e,pads;
	BOOL first_after_pad;
    int ch,lc;
#ifdef MR_FLASH
    BOOL frac;
#endif
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return 0;

    MR_IN(76)

    if (mr_mip->apbase==0 || mr_mip->apbase>256)
    {
        mr_berror(_MIPP_ MR_ERR_BASE_TOO_BIG);
        MR_OUT
        return 0;
    }

    if (!mr_mip->active)
    {
        mr_berror(_MIPP_ MR_ERR_NO_MIRSYS);
        MR_OUT
        return 0;
    }

    zero(x);
    if (mr_mip->fin) string=mr_mip->IOBUFF;
    if (mr_mip->INPLEN==0)
    { /* inputting ASCII bytes */

#ifndef MR_NO_FILE_IO

        if (mr_mip->fin)
        { /* read in characters */
            i=0;
            do
            {
                ch=fgetc(mr_mip->infile);
                if (ch==EOF) break;
                string[i++]=ch;

                if (i>=mr_mip->IOBSIZ)
                {
                    mr_berror(_MIPP_ MR_ERR_IO_OVERFLOW);
                    MR_OUT
                    return 0;
                }
            } while (ch!='\n' && ch!='\0'); 
            string[i]='\0';
        }

#endif
        forever
        { /*  get input length  */
            ch=(unsigned char)string[mr_mip->INPLEN];
            if (ch=='\0') break;
            if (mr_mip->apbase<=60 && ch=='\n') break;
            mr_mip->INPLEN++;
            if (string==mr_mip->IOBUFF && mr_mip->INPLEN>=mr_mip->IOBSIZ)
            {
                mr_berror(_MIPP_ MR_ERR_IO_OVERFLOW);
                MR_OUT
                return 0;
            }
        }
    }
    else
    { /* inputting BINARY bytes */
        if (string==mr_mip->IOBUFF && mr_mip->INPLEN>=mr_mip->IOBSIZ)
        {
            mr_berror(_MIPP_ MR_ERR_IO_OVERFLOW);
            MR_OUT
            return 0;
        }

#ifndef MR_NO_FILE_IO

        if (mr_mip->fin) for(i=0;i<mr_mip->INPLEN;i++) 
        {
            if ((ch=fgetc(mr_mip->infile))==EOF)
            {
                mr_mip->INPLEN=i;
                break;
            }
            string[i]=MR_TOBYTE(ch);
        }

#endif

    }
    n=0;
    s=PLUS;
    e=0;
#ifdef MR_FLASH
    frac=FALSE;
#endif
    if (mr_mip->INPLEN>0 && mr_mip->apbase<=60)
    { /* skip leading blanks and check sign */
#ifdef MR_FLASH
        if (string[mr_mip->INPLEN-1]=='/') mr_mip->INPLEN--;
#endif
        while (string[e]==' ') e++;
        if (string[e]=='-')
        { /* check sign */
             s=MINUS;
             e++;
        }
        if (string[e]=='+') e++;
    }

	pads=0; first_after_pad=TRUE;
    for (i=mr_mip->INPLEN-1;i>=e;i--)
    {
        ch=(unsigned char)string[i];
        if (mr_mip->apbase<=60 || mr_mip->apbase==64)
        { /* check for slash or dot and convert character to number */
#ifdef MR_FLASH
            if (mr_mip->apbase<=60 && !frac)
            {
                if (ch=='/')
                {
                    frac=TRUE;
                    copy(x,mr_mip->w0);
                    zero(x);    
                    n=0;
                    continue;
                }
                if (ch=='.')
                {
                    frac=TRUE;
                    zero(mr_mip->w0);
                    putdig(_MIPP_ 1,mr_mip->w0,n+1);
                    continue;
                }
            }
#endif
            ch+=80;
            if (mr_mip->apbase==64)
            { /* base64 */
                if (ch<=112) continue; /* ignore white space */
                if (ch>144 && ch<171) ch-=145;
                if (ch>176 && ch<203) ch-=151;
                if (ch>127 && ch<138) ch-=76;
                if (ch==123) ch=62;
                if (ch==127) ch=63;
                if (ch==141) {pads++; continue;} /* pads '=' */
            }
            else
            {
                if (ch>127 && ch<138) ch-=128;
                if (ch>144 && ch<171) ch-=135;
                if (mr_mip->apbase<=16)
                {
                    if (ch>176 && ch<203) ch-=167;
                }
                else
                {
                    if (ch>176 && ch<203) ch-=141;
                }
            }
        }
         
        if ((mr_small)ch>=mr_mip->apbase || pads>2)
        {
            mr_berror(_MIPP_ MR_ERR_BAD_FORMAT);
            MR_OUT
            return 0;
        }
       
		if (pads && first_after_pad)
		{ /* there was padding, so adjust */
			lc=ch>>(2*pads);
			first_after_pad=FALSE;
			continue;
		}

		n++;
		if (pads)
		{
			putdig(_MIPP_ 0x3f&((ch<<(6-2*pads))|lc),x,n);
			lc=(ch>>(2*pads));
			continue;
		}
		
        putdig(_MIPP_ ch,x,n);
    }

	if (pads && lc>0)
		putdig(_MIPP_ lc,x,++n);
	
    ipt=mr_mip->INPLEN;
    mr_mip->INPLEN=0;
    insign(s,x);
    mr_lzero(x);
#ifdef MR_FLASH
    mr_lzero(mr_mip->w0);
    if (frac) fpack(_MIPP_ x,mr_mip->w0,x);
#endif
    MR_OUT
    return ipt;
}

int otstr(_MIPD_ flash x,char *string)
{  /*  output a big number  */
    int s,i,n,ch,rp,nd,m;
    BOOL check;
#ifdef MR_FLASH
    int nw,dw;
#endif
    mr_lentype lx;
    BOOL done;
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return 0;

    MR_IN(75)
    if (mr_mip->apbase==0 || mr_mip->apbase>256)
    {
        mr_berror(_MIPP_ MR_ERR_BASE_TOO_BIG);
        MR_OUT
        return 0;
    }

    if (!mr_mip->active)
    {
        mr_berror(_MIPP_ MR_ERR_NO_MIRSYS);
        MR_OUT
        return 0;
    }
    n=0;
    s=exsign(x);
    insign(PLUS,x);
    lx = x->len;
    if (lx==0 && mr_mip->apbase<=60)
    {

#ifndef MR_NO_FILE_IO

        if (!mr_mip->fout)
        {
            string[0]='0';
            string[1]='\0';
        }
        else
        {
            fputc('0',mr_mip->otfile);
            fputc('\n',mr_mip->otfile);
        }
#else
        string[0]='0';
        string[1]='\0';

#endif
        MR_OUT
        return 1;
    }
    rp=0;
    if (s==MINUS && mr_mip->apbase<=60)
    {
#ifndef MR_NO_FILE_IO
        if (!mr_mip->fout) string[n]='-';
        else fputc('-',mr_mip->otfile);
#else
        string[n]='-';
#endif
        n++;
    }
#ifdef MR_FLASH
    done=FALSE;
    numer(_MIPP_ x,mr_mip->w6);
    if (mr_mip->RPOINT)
    { /* output with radix point */
        denom(_MIPP_ x,mr_mip->w5);
        if (size(mr_mip->w5)>1)
        { /* multiply up numerator to get full precision in *
           * the output. Remember position of radix point.  */
            nw=(int)(lx&MR_MSK);
            dw=(int)((lx>>MR_BTS)&MR_MSK);
            if (nw==0) nw++;
            check=mr_mip->check;
            mr_mip->check=OFF;
            if (nw>dw) mr_shift(_MIPP_ mr_mip->w5,nw-dw,mr_mip->w5);
            if (dw>nw) mr_shift(_MIPP_ mr_mip->w6,dw-nw,mr_mip->w6);
            nd=mr_mip->nib;
            if (mr_compare(mr_mip->w6,mr_mip->w5)>=0) nd--;
            copy(mr_mip->w6,mr_mip->w0);
            if (((int)mr_mip->w0->len+nd)>2*mr_mip->nib) nd=2*mr_mip->nib-(int)mr_mip->w0->len;
            mr_shift(_MIPP_ mr_mip->w0,nd,mr_mip->w0);
            divide(_MIPP_ mr_mip->w0,mr_mip->w5,mr_mip->w6);
            mr_mip->check=check;
            rp=mr_mip->pack*(nd+dw-nw);
        }
    }
#else
    copy(x,mr_mip->w6);
    done=TRUE;
#endif

    forever
    {
        nd=numdig(_MIPP_ mr_mip->w6);
        m=nd;
		if (mr_mip->apbase==64)
		{ /* add leading zeros to base64 */
			while (m%4!=0) m++;
		}
        if (rp>m) m=rp;
        for (i=m;i>0;i--)
        { 
            if (!mr_mip->fout && string==mr_mip->IOBUFF && n>=mr_mip->IOBSIZ-5)
            {
                mr_berror(_MIPP_ MR_ERR_IO_OVERFLOW);
                MR_OUT
                return n;
            }
#ifdef MR_FLASH
            if (i==rp && mr_mip->apbase<=60)
            {
#ifndef MR_NO_FILE_IO
                if (!mr_mip->fout) string[n]='.';
                else fputc('.',mr_mip->otfile);
#else
                string[n]='.';
#endif
                n++;
            }
#endif
            if (i>nd && mr_mip->apbase!=64) ch='0';
            else
            {
                ch=getdig(_MIPP_ mr_mip->w6,i);
                check=mr_mip->check;
                mr_mip->check=OFF;
                putdig(_MIPP_ 0,mr_mip->w6,i);
              /*  mr_mip->check=mr_mip->check; Nasty stupid bug! */
				mr_mip->check=check;
                if (mr_mip->apbase<=60)
                { /* convert number to character */
                    ch+=48;
                    if (ch>=58) ch+=7;
                    if (ch>=91) ch+=6;
                }
                if (mr_mip->apbase==64)
                {
                    if (ch<26) ch+=65;
                    if (ch>=26 && ch<52) ch+=71;
                    if (ch>=52 && ch<62) ch-=4;
                    if (ch==62) ch='+';
                    if (ch==63) ch='/';
                }
            }
            if (i<rp && mr_mip->apbase<=60 && ch=='0' && size(mr_mip->w6)==0) break;
#ifndef MR_NO_FILE_IO
            if (!mr_mip->fout) string[n]=MR_TOBYTE(ch);
            else fputc(MR_TOBYTE(ch),mr_mip->otfile);
#else
            string[n]=MR_TOBYTE(ch); 
#endif
            n++;
        }
        if (done) break;
#ifdef MR_FLASH
        if (mr_mip->RPOINT) break;
        denom(_MIPP_ x,mr_mip->w6);
        if (size(mr_mip->w6)==1) break;
        if (mr_mip->apbase<=60)
        {
#ifndef MR_NO_FILE_IO
            if (!mr_mip->fout) string[n]='/';
            else fputc('/',mr_mip->otfile);
#else
            string[n]='/';
#endif
            n++;
        }
        done=TRUE;
#endif
    }
/*
    if (mr_mip->apbase==64)
    {  
        while (n%3!=0) 
        {
#ifndef MR_NO_FILE_IO
            if (!mr_mip->fout) string[n]='=';
            else fputc('=',mr_mip->otfile);
#else
            string[n]='=';
#endif
            n++;
        }

    }
*/
/* Append a trailing 0 - it may be printable ascii text */    

#ifndef MR_NO_FILE_IO
    if (!mr_mip->fout) string[n]='\0';
    else if (mr_mip->apbase<=60 || mr_mip->apbase==64) fputc('\n',mr_mip->otfile);
#else
    string[n]='\0';
#endif
    insign(s,x);
    MR_OUT
    return n;
}

#ifndef MR_NO_FILE_IO

int innum(_MIPD_ flash x,FILE *filep)
{ /* convert from string to flash x */
    int n;
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return 0;

    MR_IN(1)

    mr_mip->infile=filep;
    mr_mip->fin=TRUE;
    n=instr(_MIPP_ x,NULL);
    mr_mip->fin=FALSE;

    MR_OUT
    return n;
}

int otnum(_MIPD_ flash x,FILE *filep)
{ /* convert flash to string */
    int n;
#ifdef MR_OS_THREADS
    miracl *mr_mip=get_mip();
#endif
    if (mr_mip->ERNUM) return 0;

    MR_IN(2)

    mr_mip->otfile=filep;
    mr_mip->fout=TRUE;
    n=otstr(_MIPP_ x,NULL);
    mr_mip->fout=FALSE;

    MR_OUT
    return n;
}

#endif

#else

#ifndef MR_FLASH
#ifndef MR_NO_STANDARD_IO
#ifndef MR_FP

int otnum(_MIPD_ flash x,FILE *filep)
{ /* support crude hex output only */
    int i,j,ch,n;
    BOOL leading=TRUE;
    mr_small w;
    n=0;
    if (size(x)<0) fputc('-',filep);
    for (i=(x->len&MR_OBITS)-1;i>=0;i--)
    {
        w=x->w[i];
        for (j=MIRACL-4;j>=0;j-=4)
        {
            ch=48+((w>>j)&0xF);
            if (ch==48 && leading) continue;
            leading=FALSE;
            if (ch>=58) ch+=7;
            fputc(MR_TOBYTE(ch),filep);
            n++;
        }
    }
    fputc('\n',filep);
    return n;
}

#endif
#endif
#endif

#endif
