/* Release 3.00 */

/*-------------------------------------------------------------------------*
*                                                                          *
*  r_scanf.c - MSDOS formatted input module                                *
*                                                                          *
*  COPYRIGHT (C) 1989 Jensen and Partners. All Rights Reserved             *
*  Written by Simon Knight.                                                *
*                                                                          *
*  COPYRIGHT (C) 1991 Psion PLC. All Rights Reserved                       *
*  Modified by NSM.                                                        *
*                                                                          *
*--------------------------------------------------------------------------*/

#define _USE_CONIO
#include <clib.h>

_STATIC void _vscanner(const char *param_list, va_list _v_argptr);
_STATIC va_list  _get_int(int pmod, int smod, unsigned flag, int width, va_list _v_argptr);
_STATIC va_list  _get_char(int pmod, unsigned flag, int width, int setwidth, va_list _v_argptr);
_STATIC va_list  _get_str(int pmod, unsigned flag, int width, va_list _v_argptr);
_STATIC va_list  _get_str_delim(char *s, int pmod, unsigned flag, int width, va_list _v_argptr);
_STATIC va_list  _get_pointer(int pmod, unsigned flag, va_list _v_argptr);
_STATIC int      _skip_space(void);
_STATIC int _scan_input(int scan_state);
_STATIC int  _isterm(char *s, char tc);
_STATIC int _buf_fil(FILE *st);
_STATIC int _con_fil(FILE *st);

int      _vgetc(void);
int      _vungetc(int c);

enum modifier { none,                    /* modifier prefixes */
                shortmod,                /* short integer */
                longmod,                 /* long integer */
                ldmod };                 /* LONGDOUBLE */

#define _VGETC() _vgetc()
#define _VUNGETC(c) _vungetc(c)
#define _SKIP_SPACE() _skip_space()
#define _SCAN_INPUT(scan_state) _scan_input(scan_state)

/****************************************************************************
*   this function scans the format string.                                  *
****************************************************************************/

_STATIC void _vscanner(const char *plist, va_list _v_argptr)
{
    int  n;
    int  c, i;
    int  p, f;
    int pmod, smod;
    int width, setwidth;
    unsigned flag;
    char *tlist;

    n=0;
    _s_args=0;
    _vs_count=0;                          /* zero counts */
    while((c=plist[n++]) != 0)            /* scan format string */
    {
        if(_v_argptr == NULL)
            break;
        if(isspace(c))                     /* skip white space characters */
        {                                  /* between input data */
            while(isspace(i=_VGETC()));    /* force end of file */
            if(i > 0)
            {
                _VUNGETC(i);
                continue;
            }
            else
                return;
        }
        if(c == '%')                      /* evaluate format spec */
	{
	    tlist=(char *) &plist[n];
	    goto _fmt_eval;
_fmt_eval_ret:
#pragma warn(wubd => off)
	    if(p)
            {
		n+=p;
#pragma warn(wubd => on)
                continue;
            }
            else                               /* format spec error */
            {                                  /* get next character and */
                if((c=plist[n++]) == 0)        /* check against input */
                    break;
            }
        }
        if(c != (i =_VGETC()))            /* if input char doesn't match - */
        {
            _VUNGETC(c);
            break;                        /* terminate */
        }
    }
    _vs_count++;
    return;

/****************************************************************************
*   This routine evaluates the format spec.                                 *
****************************************************************************/

_fmt_eval:

    p=0;
    flag=0;
    f=tlist[0];
    if(f == '*')
    {
	flag|=SF_SUPRESS; 		      /* suppress asignment */
        p++;
    }

/***************************************************************************
*   The next paragraph evaluates any width parameter if present. There is  *
*   no limit on field width apart from integer range.                      *
***************************************************************************/

    width=0;
    f=tlist[p++];
    while (isdigit(f))
    {
	width *= 10;
	width += f- 0x30;
        f=tlist[p++];
    }
    if(!width)
    {
	setwidth=0;			  /* width explicitly 0 */
	width=SHRT_MAX;
    }
    else
	setwidth=width;

/***************************************************************************
*   This paragraph evaluates any pointer modifiers present. The optimal    *
*   path is for no modifiers                                               *
***************************************************************************/

    pmod=none;

/***************************************************************************
*   This paragraph evaluates any size modifiers present. The optimal       *
*   path is for no modifiers                                               *
***************************************************************************/

    switch(f)
    {
        case 0 :                          /* incomplete spec - return */
        case '\ ':
	    p=0;
	    goto _fmt_eval_ret;
        case 'h':
            smod=shortmod;                 /* short integer */
            f=tlist[p++];
            break;
        case 'L':
            smod=ldmod;                    /* LONGDOUBLE */
            f=tlist[p++];
            break;
        case 'l':
            smod=longmod;                  /* long integer */
            f=tlist[p++];
            break;
        default:
            smod=none;
            break;
    }

/***************************************************************************
*   This paragraph is the main type switch. The argument pointer is taken  *
*   from the argument list and input and conversion functions called, where*
*   if not disabled, an assignment is made.                                *
***************************************************************************/

    switch(f)
    {
        case 'i':
	    flag|=SF_WILD;
	    goto _do_int;
            break;
        case 'd':
	    goto _do_int;
            break;
        case 'o':
	    flag|=SF_OCT;
	    goto _do_int;
            break;
        case 'X':
        case 'x':
	    flag|=SF_HEX;
	    goto _do_int;
            break;
        case 'U':
	    flag|=SF_UPPER;
        case 'u':
	    flag|=SF_NSIGNED;
_do_int:
	    _v_argptr=_get_int(pmod, smod, flag, width, _v_argptr);
            break;
        case 'E':                              /* real types */
        case 'e':
        case 'f':
        case 'G':
	case 'g':
            _v_argptr=_real_in(pmod, smod, flag, width, _v_argptr);
            break;
        case 'c':                              /* character */
	    _v_argptr=_get_char(pmod, flag, width, setwidth, _v_argptr);
            break;
        case 's':                              /* string */
	    _v_argptr=_get_str(pmod, flag, width, _v_argptr);
            break;
        case '[':                              /* deliminated string */
	    _v_argptr=_get_str_delim(&tlist[p], pmod, flag, width, _v_argptr);
	    while((tlist[p++] != ']') && (p-1));
            break;
        case 'n':                              /* character count */
	    if(smod == longmod)
		*(va_arg(_v_argptr, long *)) = _vs_count;
	    else
		*(va_arg(_v_argptr, int *)) = _vs_count;
	    break;
	case 'p':			       /* pointer */
	    flag|=(SF_HEX);
	    pmod=none;
	    goto _do_int;
        default:
	    p=0;
	    goto _fmt_eval_ret;
    }
    goto _fmt_eval_ret; 		       /* return */
}

_STATIC int _skip_space()
{
    int i;

    while(isspace(i=_VGETC()));           /* skip white space in stream */
    if(i >= 0)
    {
        _VUNGETC(i);
        return(0);
    }
    else if(_s_args == 0)
        longjmp(_EOFhandler, 1);
    return(-1);
}

_STATIC int _scan_input(int scan_state)
{
    int c;

    c=_VGETC();
    if(c != -1)
        return(c);
    if((_s_args == 0) && (scan_state == 0))
        longjmp(_EOFhandler, 1);
    return(-1);
}

/****************************************************************************
*   This function scans the input stream and converts to an integer.        *
****************************************************************************/

_STATIC va_list  _get_int(int pmod, int smod, unsigned flag, int width, va_list _v_argptr)
{
    unsigned long temp;
    int c, good_scan;
    int negative;
    void *iptr;

    temp=0;
    good_scan=0;
    negative=FALSE;
    if(_SKIP_SPACE() == -1)
        return(_v_argptr);
    c=_SCAN_INPUT(good_scan);
    if((c == '+') && (width))
    {
        if((c=_SCAN_INPUT(good_scan)) == -1)
            return(_v_argptr);
	--width;
    }
    else if((c == '-') && (width))
    {                                    /* number is negative */
        negative=TRUE;
        if((c=_SCAN_INPUT(good_scan)) == -1)
            return(_v_argptr);
	--width;
    }
    if(flag&SF_WILD)
    {                                    /* get dec oct or hex */
	if((c =='0') && (width))
        {
	    flag|=SF_OCT;
	    --width;
	    good_scan=1;
            c=_SCAN_INPUT(good_scan);
            c=tolower(c);
	    if(c == 'x')
	    {
		flag|=SF_HEX;
                if((c=_SCAN_INPUT(good_scan)) == -1)
                    return(_v_argptr);
		--width;
            }
        }
    }
    if(flag&SF_HEX)			     /* convert hex */
    {
	if((c =='0') && (width))
        {
	    --width;
	    good_scan=1;
            c=_SCAN_INPUT(good_scan);
            c=tolower(c);
	    if(c == 'x')
	    {
                if((c=_SCAN_INPUT(good_scan)) == -1)
                    return(_v_argptr);
		--width;
            }
        }
	while((isxdigit(c=tolower(c))) && (width >0))
        {
            good_scan=1;
            temp*=16;
            if(!isdigit(c))
                c-=87;
            else
                c-='0';
            temp+=c;
            c=_SCAN_INPUT(good_scan);
	    --width;
        }
    }
    else if(flag&SF_OCT)				 /* convert octal */
    {
	while((c >= '0') && (c <= '7') && (width>0))
        {
            good_scan=1;
            temp*=8;
            temp+=c-'0';
            c=_SCAN_INPUT(good_scan);
	    --width;
        }
    }
    else                                    /* convert decimal */
    {
	while((isdigit(c)) && (width>0))
        {
            good_scan=1;
            temp*=10;
            temp+=c-'0';
            c=_SCAN_INPUT(good_scan);
	    --width;
        }
    }
    _VUNGETC(c);
    if((!(flag&SF_NSIGNED)) && (negative))	/* ignore '-' if unsigned */
        temp*= -1;
    if(!good_scan)
        return(NULL);
    if(!(flag&SF_SUPRESS))	/* assign field unless suppressed */
    {
	if(flag&SF_UPPER)
            smod=longmod;
	iptr = (va_arg(_v_argptr, void *));
        switch(smod)
        {
            case shortmod:
                *((short *) iptr) = (short) temp;
                break;
            case longmod:
                *((long *) iptr) = (long) temp;
                break;
            default:
                *((int *) iptr) = (int) temp;
                break;
        }
        _s_args++;                          /* inc argument counter */
    }
    return(_v_argptr);
}

/****************************************************************************
*   This function scans the input stream and converts to real.              *
****************************************************************************/

#pragma warn(wall => off)

va_list _rreal_in(int pmod, int smod, unsigned flag, int width, va_list _v_argptr)
{
    int n, q, c, good_scan;
    LONGDOUBLE volatile dtemp;
    char  _fmt_buf[64]; 	      /* buffer for formatted number */
    void *dptr;

    n=0;
    good_scan=0;
    if(_SKIP_SPACE() == -1)
        return(_v_argptr);
    c=_SCAN_INPUT(good_scan);
    while(1)
    {
	if((c == '+') || (c == '-'))	   /* leading sign */
        {
	    _fmt_buf[n++]= (char) c;
            if((c=_SCAN_INPUT(good_scan)) == -1)
                return(_v_argptr);
	    --width;
        }                        /* width check always after _VGETC */
        for(q=0; q<2; q++)
        {
	    while((isdigit(c)) && (width))     /* get digits */
            {
                good_scan=1;
		_fmt_buf[n++]= (char) c;
                c=_SCAN_INPUT(good_scan);
		--width;
            }
	    if((c == DEC_POINT) && (q == 0) && (width))   /* decimal point */
            {
		_fmt_buf[n++]= (char) c;
                if((c=_SCAN_INPUT(good_scan)) == -1)
                    break;
		--width;
            }
            else if(q == 0)
                break;
        }                  /* go back for more digits if first iteration */
	if(!width)
            break;         /* exponent */
        if((c == 'E') || (c == 'e') || (c == 'D') || (c == 'd'))
        {
	    _fmt_buf[n++]= (char) c;
            c=_SCAN_INPUT(good_scan);
	    --width;
        }
        else
            break;
	if(!width)	    /* exponent leading sign */
            break;
	if((c == '+') || (c == '-'))
        {
	    _fmt_buf[n++]= (char) c;
            c=_SCAN_INPUT(good_scan);
	    --width;
        }                   /* exponent digits */
	while((isdigit(c)) && (width))
        {
	    _fmt_buf[n++]= (char) c;
            c=_SCAN_INPUT(good_scan);
	    --width;
        }
        break;
    }
    _fmt_buf[n]=0;           /* terminate string */
    _VUNGETC(c);
    if(!good_scan)
        return(NULL);
    if(!(flag&SF_SUPRESS))
    {
	dtemp=atofl(_fmt_buf);	 /* convert and assign if not suppressed */
	dptr = (va_arg(_v_argptr, void *));
        switch(smod)
        {
            case ldmod :
                *((LONGDOUBLE *) dptr) = dtemp;
                break;
            case longmod :
                *((double *) dptr) = (double) dtemp;
                break;
            default :
                *((float *) dptr) = (float) dtemp;
                break;
        }
        _s_args++;
    }
    return(_v_argptr);
}

/****************************************************************************
*   This function scans the input stream for the next width characters even *
*   if white space.                                                         *
****************************************************************************/

_STATIC va_list  _get_char(int pmod, unsigned flag, int width, int setwidth, va_list _v_argptr)
{
    int c, n, good_scan;
    char *array;

    n=0;
    good_scan=0;
    if(!setwidth)		   /* width = 1 if not set */
	width=1;
    if(!(flag&SF_SUPRESS))	   /* if not suppressed get char pointer */
    {
	array=va_arg(_v_argptr, char *);
    }
    while(width)
    {                              /* scan input */
        if((c=_SCAN_INPUT(good_scan)) == -1)
            break;
	--width;
	good_scan=1;
	if(!(flag&SF_SUPRESS))	   /* assign if not suppressed */
        {
	    array[n++]=(char) c;              /* terminate string if not suppressed */
        }
    }
    if(good_scan)
      ++_s_args;
    else
      return(NULL);
    return(_v_argptr);
}

/****************************************************************************
*   This function scans the input stream for the next width characters      *
*   until it finds a white space character.                                 *
****************************************************************************/

_STATIC va_list  _get_str(int pmod, unsigned flag, int width, va_list _v_argptr)
{
    int c, n, good_scan;
    char *array;

    n=0;
    good_scan=0;
    if(_SKIP_SPACE() == -1)
        return(_v_argptr);
    c=_SCAN_INPUT(good_scan);
    if(!(flag&SF_SUPRESS))	 /* get char pointer if not suppressed */
    {
	array=va_arg(_v_argptr, char *);
    }
    while (width)
    {
        if(isspace(c))   /* terminate if white-space or EOF */
            break;
	good_scan=1;
	if(!(flag&SF_SUPRESS))   /* assign if not suppressed */
        {
	    array[n++]=(char) c;
        }
        if((c=_SCAN_INPUT(good_scan)) == -1)
            break;
	--width;
    }
    _VUNGETC(c);
    if(!(flag&SF_SUPRESS))       /* terminate string if not suppressed */
    {
	array[n]='\0';              /* terminate string if not suppressed */
    }
    if(good_scan)
      ++_s_args;
    else
      return(NULL);
    return(_v_argptr);
}

/****************************************************************************
*   This function scans the input stream for the next width characters      *
*   until it finds a white space character.                                 *
****************************************************************************/

_STATIC va_list  _get_str_delim(char *s, int pmod, unsigned flag , int width, va_list _v_argptr)
{
    int c, n, good_scan;
    char *array;

    n=0;
    good_scan=0;
    if((c=_SCAN_INPUT(good_scan)) == -1)
        return(_v_argptr);
    if(!(flag&SF_SUPRESS))	       /* get char pointer unless suppressed */
    {
	array=va_arg(_v_argptr, char *);
    }
    while(width)
    {
        if(_isterm(s, (char)c))  /* terminate if a deliminating character */
            break;
	good_scan=1;
	if(!(flag&SF_SUPRESS))	 /* asign if not suppressed */
        {
	    array[n++]=(char) c;
        }
        if((c=_SCAN_INPUT(good_scan)) == -1)
            break;
	width--;
    }
    _VUNGETC(c);
    if(!(flag&SF_SUPRESS))
    {
	array[n]='\0';              /* terminate string if not suppressed */
    }
    if(good_scan)
      ++_s_args;
    else
      return(NULL);
    return(_v_argptr);
}

/****************************************************************************
*   This function scans the format string for deliminating characters.      *
*   It returns TRUE if the character is a deliminator.                      *
****************************************************************************/

_STATIC int  _isterm(char  *s, char tc)
{
    int n;
    int first;

    n=0;
    first=TRUE;
    if(s[n] == '^')              /* charcaters in set are deliminators */
    {
        n++;
        while(((s[n] != ']') || (first)) && (s[n]))
        {
            first=FALSE;
            if(tc == s[n++])
                return(TRUE);
        }
        return(FALSE);
    }                            /* characters not in set are deliminators */
    else
    {
        while(((s[n] != ']') || (first)) && (s[n]))
        {
            first=FALSE;
            if(tc == s[n++])
                return(FALSE);
        }
        return(TRUE);
    }
}

#pragma warn(wall => on)
#pragma warn(wpnu => off)

/*-------------------------------------------------------------------------*
*  scanf.c   - formatted input from stdin                                  *
*  This function provides formatted input from stdin for a variable        *
*  number of arguments.                                                    *
*--------------------------------------------------------------------------*/

int  scanf(const char *format, ...)
{
    va_list _v_argptr;
    int temp_buf, ret;

    temp_buf=0;
    if(stdin->_size == 0)
    {
        char buffer[PF_TEMP_BUF];
        stdin->_flag|=_F_RST;
        _vp_setbuf(stdin, buffer);
        stdin->_cnt=0;
        temp_buf=1;
    }
    _vs_fill=_filler;                /* set put_back function */
    _stream=stdin;
    va_start(_v_argptr, format);     /* initialise argument pointer */
    if(setjmp(_EOFhandler))          /* EOF in input function arrives here */
    {
        ret=-1;
        goto sf_out;
    }
    _vscanner(format, _v_argptr);    /* do it */
    ret=_s_args;
sf_out:
    if(temp_buf)
    {
        fflush(stdin);
        _vp_freebuf(stdin);
    }
    return(ret);                 /* return number of fields assigned */
}

/*-------------------------------------------------------------------------*
*  cscanf.c   - formatted input from console                               *
*  This function provides formatted input from console for a variable      *
*  number of arguments.                                                    *
*--------------------------------------------------------------------------*/

int _con_fil(FILE *st)
{
    int c;

    st->_ptr=st->_base;
    st->_cnt=st->_size;
    c=getche();
    *st->_ptr= (char ) c;
    if(c == 13)
      ungetch(10);
    if(c == _CTLZ)
        return(EOF);
    return(1);
}

int  cscanf(const char *format, ...)
{
    va_list _v_argptr;
    FILE cst;
    char cbuf[2];

    cst._cnt=0;
    cst._size=1;
    cst._base=cbuf;
    cst._ptr=cbuf;
    _stream=&cst;
    _vs_fill=_con_fil;               /* set filler function */
    va_start(_v_argptr, format);     /* initialise argument pointer */
    if(setjmp(_EOFhandler))          /* EOF in input function arrives here */
        return(EOF);
    _vscanner(format, _v_argptr);    /* do it */
    return(_s_args);                 /* return number of fields assigned */
}

/*-------------------------------------------------------------------------*
*  sscanf.c - formatted input from buffer                                  *
*  This function provides formatted input from a character buffer for a    *
*  variable number of arguments.                                           *
*--------------------------------------------------------------------------*/

int _buf_fil(FILE *st)
{
    return(-1);
}

int  sscanf(const char *s, const char *format, ...)
{
    va_list _v_argptr;
    FILE sst;

    _vs_fill=_buf_fil;               /* set filler function */
    sst._cnt=strlen(s)+1;
    sst._ptr=(char *) s;
    sst._flag=_F_BIN;
    _stream=&sst;
    va_start(_v_argptr, format);     /* initialise argument pointer */
    if(setjmp(_EOFhandler))          /* EOF arrives here */
        return(EOF);
    _vscanner(format, _v_argptr);    /* do it */
    return(_s_args);                 /* return number of fields assigned */
}

/*-------------------------------------------------------------------------*
*  fscanf.c - formatted input from stream                                  *
*  This function provides formatted input from stream for a variable       *
*  number of arguments.                                                    *
*--------------------------------------------------------------------------*/

int  fscanf(FILE *st, const char *format, ...)
{
    va_list _v_argptr;
    int temp_buf, ret;

    temp_buf=0;
    if((st->_flag&_F_OUT) || (!(st->_flag&_F_READ)))
        return(-1);
    if(st->_size == 0)
    {
        char buffer[PF_TEMP_BUF];
        st->_flag|=_F_RST;
        _vp_setbuf(st, buffer);
        st->_cnt=0;
        temp_buf=1;
    }
    _vs_fill=_filler;                /* set put_back function */
    _stream=st;
    va_start(_v_argptr, format);     /* initialise argument pointer */
    if(setjmp(_EOFhandler))          /* EOF in input function arrives here */
    {
        ret=-1;
        goto fsf_out;
    }
    _vscanner(format, _v_argptr);    /* do it */
    ret=_s_args;
fsf_out:
    if(temp_buf)
    {
        fflush(st);
        _vp_freebuf(st);
    }
    return(ret);                 /* return number of fields assigned */
}

/*-------------------------------------------------------------------------*
*  vscanf.c  - formatted input from stdin                                  *
*  This function provides formatted input from stdin for a variable        *
*  number of arguments.                                                    *
*--------------------------------------------------------------------------*/

int  vscanf(const char *format, va_list argptr)
{
    va_list _v_argptr;
    int temp_buf, ret;

    temp_buf=0;
    if(stdin->_size == 0)
    {
        char buffer[PF_TEMP_BUF];

        stdin->_flag|=_F_RST;
        _vp_setbuf(stdin, buffer);
        stdin->_cnt=0;
        temp_buf=1;
    }
    _vs_fill=_filler;                /* set put_back function */
    _stream=stdin;
    _v_argptr=argptr;                /* initialise argument pointer */
    if(setjmp(_EOFhandler))          /* EOF in input function arrives here */
    {
        ret=-1;
        goto vsf_out;
    }
    _vscanner(format, _v_argptr);    /* do it */
    ret=_s_args;
vsf_out:
    if(temp_buf)
    {
        fflush(stdin);
        _vp_freebuf(stdin);
    }
    return(ret);                 /* return number of fields assigned */
}

/*-------------------------------------------------------------------------*
*  vsscanf.c - formatted input from buffer                                  *
*  This function provides formatted input from a character buffer for a    *
*  variable number of arguments.                                           *
*--------------------------------------------------------------------------*/

int  vsscanf(const char *s, const char *format, va_list argptr)
{
    va_list _v_argptr;
    FILE sst;

    _vs_fill=_buf_fil;               /* set filler function */
    sst._cnt=strlen(s)+1;
    sst._ptr=(char *) s;
    sst._flag=_F_BIN;
    _stream=&sst;
    _v_argptr=argptr;                /* initialise argument pointer */
    if(setjmp(_EOFhandler))          /* EOF arrives here */
        return(EOF);
    _vscanner(format, _v_argptr);    /* do it */
    return(_s_args);                 /* return number of fields assigned */
}

/*-------------------------------------------------------------------------*
*  vfscanf.c - formatted input from stream                                  *
*  This function provides formatted input from stream for a variable       *
*  number of arguments.                                                    *
*--------------------------------------------------------------------------*/

int  vfscanf(FILE *st, const char *format, va_list argptr)
{
    va_list _v_argptr;
    int temp_buf, ret;

    temp_buf=0;
    if((st->_flag&_F_OUT) || (!(st->_flag&_F_READ)))
        return(-1);
    if(st->_size == 0)
    {
        char buffer[PF_TEMP_BUF];
        st->_flag|=_F_RST;
        _vp_setbuf(st, buffer);
        st->_cnt=0;
        temp_buf=1;
    }
    _vs_fill=_filler;                /* set put_back function */
    _stream=st;
    _v_argptr=argptr;                /* initialise argument pointer */
    if(setjmp(_EOFhandler))          /* EOF in input function arrives here */
    {
        ret=-1;
        goto vfsf_out;
    }
    _vscanner(format, _v_argptr);    /* do it */
    ret=_s_args;
vfsf_out:
    if(temp_buf)
    {
        fflush(st);
        _vp_freebuf(st);
    }
    return(ret);                 /* return number of fields assigned */
}

