#include <stdio.h>               /* Standard I/O package                  */
#include <ctype.h>               /* Character classification macros       */
#include <stdarg.h>              /* Standard argument functions           */
#include <string.h>              /* strxxx functions                      */
#include <stdlib.h>              /* strtod function                       */

#include "file2.h"               /* Secondary file data                   */
#include "internal.h"            /* Internal definitions                  */

#define ASCII       256          /* Number of characers in the table      */
#define REALENGTH    64          /* Maximum length of a real number buffer*/

#define DEFAULT  0               /* No size was specified                 */
#define SHORT    1               /* Short word format was specified       */
#define LONG     2               /* Long word format was specified        */
#define LONGLONG 3               /* A long-long floating point was given  */

static int fFoundEOF;            /* non-zero if EOF detected              */
static FILE *fpInput;            /* Pointer to the input file             */
static char *pacTable;           /* Pointer to the ascii [...] table      */
static int fSuppressedStore;     /* non-zero if "*" was specified         */
static int nWidth;               /* field width, if specified             */
static int nItemCount;           /* count of items matched & assigned     */
static int nArgSize;             /* integer input size                    */
static unsigned int nCharCount;  /* actual count of input characters read */
static va_list argp;             /* Current argument pointer              */

#define QUEUE_SIZE 1                /* I need only 1 character, but still */
static int  nPushBack;              /* Item count in the push-back buffer */
static char auchQueue[QUEUE_SIZE];  /* Queue for the push-back buffer     */

/*
 *  Function prototypes
 */

typedef int (*pfnString) (int);

static void PushBack (int nChar);
static int  NextInputChar (void);
static int  GoodWidth (void);
static int  NextFieldChar(void);
static int  MatchInput (int nMatch);
static void SkipBlanks (void);
static int  CollectString_Space (int nChar);
static int  CollectString_All (int nChar);
static int  CollectString_Table (int nChar);
static void CollectString (int fString, pfnString pfnTest);
static void StoreFixedPt (unsigned long ulValue);
static void CollectFixedPt (int nBase);
static void CollectFloatPnt (void);
static const char *ScanStringRange (const char *pszFormat);

/*************************************************************************
 *
 * Function:    PushBack
 *
 * Description:
 *    Return the character to the "input" stream. The character sequence
 *    is maintained in a queue so that it is returned to the caller of
 *    the NextInputChar routine.
 *
 * Entry:
 *    nChar    - Character to be returned to the input.
 *
 * Returns:
 *    none.
 */

static void PushBack (int nChar)
    {
    if (nChar != EOF && nPushBack < QUEUE_SIZE)
        {
	auchQueue [nPushBack++] = (unsigned char) nChar;
	--nCharCount;
        }
    }

/*************************************************************************
 *
 * Function:    NextInputChar
 *
 * Description:
 *    Return the next character from the input file.
 *
 * Entry:
 *    none.
 *
 * Returns:
 *    The character or EOF to indicate an end of file condition.
 */

static int NextInputChar (void)
    {
    int nAnswer;
/*
 *  Deal with the push-back queue for the input.
 */
    if (nPushBack != 0)
        {
	nAnswer = (int) (unsigned int) auchQueue [--nPushBack];
	++nCharCount;
        }
/*
 *  If EOF is not pending then read the next character and check for the
 *  EOF condition.
 */
    else
        {
	if (fFoundEOF == 0)
	    {
	    nAnswer = getc (fpInput);
	    if (nAnswer == EOF)
	        {
		fFoundEOF = 1;
	        }
	    }
/*
 *  If EOF is pending then adjust the result to be the EOF sequence.
 */
	if (fFoundEOF)
	    {
	    nAnswer = EOF;
	    }
	else
	    {
	    ++nCharCount;
	    }
        }
/*
 *  Return the result of the operation.
 */
    return (nAnswer);
    }

/*************************************************************************
 *
 * Function:    GoodWidth
 *
 * Description:
 *    Determine if the input field width is valid.
 *
 * Entry:
 *    none.
 *
 * Returns:
 *    0 if no more characters are allowed
 *    1 if more characters are allowed in the field width
 */

static int GoodWidth (void)
    {
    int  nAnswer = 0;
/*
 *  If the field width has not been specified then the character is
 *  always valid.
 */    
    if (nWidth == 0)
        {
	nAnswer = 1;
        }
/*
 *  Determine if the width will allow the character. Accept the character
 *  if possible.
 */
    else
        {
	if (nWidth > 0)
	    {
	    --nWidth;
	    nAnswer = 1;
	    }
        }
/*
 *  Return the valid status
 */
    return (nAnswer);
    }

/***********************************************************************
 *
 * Function:   NextFieldChar
 *
 * Description:
 *     Fetch the next character should the input width allow the character
 *     to be read.
 *
 * Entry:
 *     none.
 *
 * Returns:
 *     The character or EOF to indicate that there are no more characters
 *     in the input or the field.
 */

static int NextFieldChar(void)
    {
    int  nAnswer;
/*
 *  If the character is valid for the width then obtain it.
 */
    if (GoodWidth())
        {
	nAnswer = NextInputChar();
        }
/*
 *  Return the EOF character
 */
    else
        {
	nAnswer = EOF;
        }

    return (nAnswer);
    }

/*************************************************************************
 *
 * Function:    MatchInput
 *
 * Description:
 *    Discard the characters which match the input argument.
 *
 * Input:
 *    nMatch  - Character to be matched
 *
 * Returns:
 *    EOF   if the input has EOF
 *    0     if a match was found
 *    1     if a match was not found
 */

static int MatchInput (int nMatch)
    {
    int nChar;
    int nAnswer;
/*
 *  Fetch the next character from the input
 */
    nChar = NextFieldChar();
    if (nChar == EOF)
        {
	nAnswer = EOF;
        }
/*
 *  If the character matches the input then set the state to 1
 *  and discard the character. Otherwise reset the state to 0
 *  and put the characte back on the input queue.
 */
    else
        {
	if (nChar == nMatch)
	    {
	    nAnswer = 1;
	    }
	else
	    {
	    nAnswer = 0;
	    PushBack (nChar);
	    }
        }
/*
 *  Return the result of the comparison.
 */
    return (nAnswer);
    }

/**************************************************************************
 *
 * Function:    SkipBlanks
 *
 * Description:
 *    Discard whitespace from the input stream. The characters are discarded
 *    if "isspace" matches the input character.
 *
 * Entry:
 *    none.
 *
 * Returns:
 *    none.
 */

static void SkipBlanks (void)
    {
    int  nChar;
/*
 *  Discard the characters until a non-space is found.
 */
    do
        {
	nChar = NextInputChar();
        }
    while (nChar != EOF && isspace (nChar));
/*
 *  Return the character to the input stream.
 */
    PushBack (nChar);
    }

/****************************************************************************
 *
 * Function:   CollectString_Space
 *
 * Description:
 *   Determine if the input character is a member of the space set.
 *
 * Entry:
 *    nChar   - Current input character
 *
 * Returns:
 *    0 to indicate the character is a space
 *  <>0 to indicate the character is not a space
 */

static int CollectString_Space (int nChar)
    {
    return (isspace (nChar) ? 1 : 0);
    }

/****************************************************************************
 *
 * Function:   CollectString_All
 *
 * Description:
 *   Determine if the input character is valid for a %c input routine. This
 *   is always true and the result will accept all characters until the
 *   limit is reached for the input count.
 *
 * Entry:
 *    nChar   - Current input character
 *
 * Returns:
 *    1 to indicate the character is acceptable.
 */

static int CollectString_All (int nChar)
    {
    return (1);
    }

/****************************************************************************
 *
 * Function:   CollectString_Table
 *
 * Description:
 *   Determine if the character is a member of the input table set.
 *
 * Entry:
 *    nChar   - Current input character
 *
 * Returns:
 *    0 to indicate the character is a member
 *  <>0 to indicate the character is not a member
 */

static int CollectString_Table (int nChar)
    {
    return (pacTable [nChar]);
    }

/****************************************************************************
 *
 * Function:   CollectString
 *
 * Description:
 *   Read the character string into the buffer at the next parameter
 *   address.
 *
 * Entry:
 *    fAppendMode  - Is this a single character (%c) or not.
 *                   0 = string (%s) input
 *                   1 = character (%c) input
 *
 * Returns:
 *    none.
 */

static void CollectString (int fString, pfnString pfnTest)
    {
    char *pchPtr;
    char *pchStart;
    int  nChar;
/*
 *  Fetch the pointer to the argument.
 */
    if (!fSuppressedStore)
        {
	pchPtr = va_arg (argp, char *);
        }
    else
        {
	pchPtr = NULL;
        }
    pchStart = pchPtr;
/*
 *  If this is character input and the field width has not been
 *  specified then default the width to a single character. This means
 *  that the user gave "%c".
 */
    if (!fString && nWidth == 0)
        {
	nWidth = 1;
        }
/*
 *  Fetch the firat character. Process all characters until EOF has
 *  been found.
 */
    pchStart = pchPtr;
    nChar    = NextFieldChar();
    while (nChar != EOF)
        {
/*
 *  Look for a match to the character table
 */
	if (!(*pfnTest) (nChar))
	    {
	    PushBack (nChar);
	    break;
	    }
/*
 *  Store the next character into the input string.
 */
	if (!fSuppressedStore)
	    {
	    *pchPtr++ = (char) nChar;
	    }
	nChar = NextFieldChar();
        }
/*
 *  Store the terminating NUL character should this be desired.
 */
    if (fString && !fSuppressedStore)
        {
	*pchPtr = '\0';
        }
/*
 *  Count the nul character if any string was processed
 */
    if (pchStart != pchPtr)
        {
	++nItemCount;
        }
    }

/************************************************************************
 *
 * Function:     StoreFixedPt
 *
 * Description:
 *    Store the next fixed point number
 *
 * Entry:
 *    ulValue   - Value to be stored
 *
 * Returns:
 *    none.
 */

static void StoreFixedPt (unsigned long ulValue)
    {
/*
 *  If the item is not suppressed then store it based upon the
 *  size field.
 */
    if (!fSuppressedStore)
        {
	if (nArgSize == SHORT)
	    {
	    unsigned short *pusPtr = va_arg (argp, unsigned short *);
	    *pusPtr = (unsigned short) ulValue;
	    }
	else
	    {
	    unsigned long *pulPtr = va_arg (argp, unsigned long *);
	    *pulPtr = ulValue;
	    }
        }
    }

/************************************************************************
 *
 * Function:     CollectFixedPt
 *
 * Description:
 *    Obtain the numerical value from the input stream.
 *
 * Entry:
 *    nBase   - Base for the input
 *
 * Returns:
 *    none.
 */

static void CollectFixedPt (int nBase)
    {
    int           nChar;
    int           nDigit;
    int           fNeg;
    unsigned long ulNumber;
/*
 *  Initialize to process the item
 */
    ulNumber = 0L;
    fNeg     = 0;
/*
 *  Fetch the first character from the input.
 */
    nChar = NextFieldChar();
    fNeg  = 0;
    if (nBase == 10)
        {
	if (nChar == '-')
	    {
	    fNeg  = 1;
	    nChar = NextFieldChar();
	    }
/*
 *  Ignore leading plus character
 */
	else
	    {
	    if (nChar == '+')
	        {
		nChar = NextFieldChar();
	        }
	    }
        }
/*
 *  Process the digits
 */
    while (nChar != EOF)
        {
	if (isdigit (nChar))
	    {
	    nDigit = nChar - '0';
	    }
/*
 *  Process the lower case alphabetics
 */
	else
	    {
	    if (islower (nChar))
	        {
		nDigit = nChar - ('a' - 10);
	        }
/*
 *  Process the upper case alphabetics
 */
	    else
	        {
		if (isupper (nChar))
		    {
		    nDigit = nChar - ('A' - 10);
		    }
/*
 *  All other characters are invalid
 */
		else
		    {
		    nDigit = nBase;
		    }
	        }
	    }
/*
 *  If the valid is too large then stop processing
 */
	if (nDigit >= nBase)
	    {
	    PushBack (nChar);
	    break;
	    }
	    
	ulNumber *= (unsigned long) nBase;
	ulNumber += (unsigned long) nDigit;
	nChar     = NextFieldChar();
        }
/*
 *  Correct the sign of the value
 */
    if (fNeg)
        {
	ulNumber = (unsigned long) -((long) ulNumber);
        }
/*
 *  Store the resulting numeric value if the item is not suppressed
 */
    if (!fSuppressedStore)
        {
	StoreFixedPt (ulNumber);
	++nItemCount;
        }
    }

/*************************************************************************
 *
 * Function:   CollectFloatPnt
 *
 * Description:
 *    Scan and store the next floating point number.
 *
 * Entry:
 *    none.
 *
 * Returns:
 *    none.
 */

static void CollectFloatPnt (void)
    {
    char  abBuffer [REALENGTH];
    int   nIndex;
    int   nChar;
    int   fDigits;
/*
 *  Initialize to process the input
 */
    nIndex  = 0;
    fDigits = 0;
    nChar   = NextFieldChar();
/*
 *  Ensure that the input is legal length
 */
    if (nWidth == 0 || nWidth >= REALENGTH)
        {
	nWidth = REALENGTH - 1;
        }
/*
 *  Fetch the leading sign character
 */
    if (nChar == '+' || nChar == '-')
        {
	abBuffer [nIndex++] = (char) nChar;
	nChar = NextFieldChar();
        }
/*
 *  Fetch the whole mantissa digits
 */
    while (isdigit (nChar))
        {
	abBuffer [nIndex++] = (char) nChar;
	nChar   = NextFieldChar();
	fDigits = 1;
        }
/*
 *  Fetch the radix point
 */
    if (nChar == '.')
        {
	abBuffer [nIndex++] = (char) nChar;
	nChar   = NextFieldChar();
	fDigits = 0;
/*
 *  Fetch the fractional digits of the mantissa
 */
	while (isdigit (nChar))
	    {
	    abBuffer [nIndex++] = (char) nChar;
	    nChar   = NextFieldChar();
	    fDigits = 1;
	    }
        }
/*
 *  Fetch the exponent seperator
 */
    if (fDigits && (nChar == 'E' || nChar == 'e'))
        {
	abBuffer [nIndex++] = (char) nChar;
	nChar   = NextFieldChar();
	fDigits = 0;
/*
 *  Fetch the sign of the exponent
 */
	if (nChar == '+' || nChar == '-')
	    {
	    abBuffer [nIndex++] = (char) nChar;
	    nChar   = NextFieldChar();
	    fDigits = 1;
	    }
/*
 *  Collect the digits of the exponent.
 *  If the sign of the exponent is not specified then insert a leading
 *  plus sign.
 */
	while (isdigit (nChar))
	    {
	    if (fDigits == 0)
	        {
		abBuffer [nIndex++] = '+';
		fDigits = 1;
	        }
	    abBuffer [nIndex++] = (char) nChar;
	    nChar  = NextFieldChar();
	    }
        }
/*
 *  Store the terminating nul character and return the terminating
 *  character to the input stream.
 */
    abBuffer [nIndex++] = '\0';
    PushBack (nChar);    /* last character is next read */
/*
 *  At this point, collect the numeric value
 */
    if (!fSuppressedStore)
        {
	if (nArgSize == SHORT)
	    {
	    float *pflt = va_arg (argp, float *);
	    *pflt = (float) strtod (abBuffer, NULL);
	    }
	else
	    {
	    double *pdbl = va_arg (argp, double *);
	    *pdbl = strtod (abBuffer, NULL);
	    }
	++nItemCount;
        }
    }

/***************************************************************************
 *
 * Function:   ScanStringRange
 *
 * Description:
 *   Process the enumerated character sequence. This allows the scanner
 *   to recogize a range of characters when they are delimted in brackets
 *   such as [abc].
 *
 *   If the first character in the group is "^", the collection is a set of
 *   rejection charracters. The set is negated.
 *
 *   To process the ] character, it must be the first character (possibly
 *   following the "^" character.)
 *
 *   A range of characters may be used if seperated by a "-" character.
 *   The sequence [a-z] represents the set of lower case characters.
 *
 * Input:
 *   pszFormat   - Pointer to the format string
 *
 * Returns:
 *   A pointer to the next format character following the set.
 */

static const char *ScanStringRange (const char *pszFormat)
    {
    char acArray [ASCII];
    int  nLower, nUpper;
    int  nFmtChar;
/*
 *  Store the pointer to the table buffer area
 */
    pacTable = acArray;      /* Pointer to the working table buffer */
/*
 *  If the next character is ^ then reverse the test and fill the buffer
 *  with the accept sequence so that when the reject characters are inserted
 *  they will toggle to reject status.
 */
    nFmtChar = (int) *pszFormat++;
    if (nFmtChar == '^')
        {
	nFmtChar = (int) *pszFormat++;
	memset (pacTable, '\1', ASCII);
        }
    else
        {
	memset (pacTable, '\0', ASCII);
        }
/*
 *  Accept the ] character in the string if it occurs first.
 */
    if (nFmtChar == ']')
        {
	pacTable [']'] ^= 1;
	nFmtChar = (int) *pszFormat++;
        }
/*
 *  Accept the - character in the string if it occurs first. Some
 *  implementations do not allow a range. If this is the case then delete
 *  this test and its subsequence TRUE clause.
 */
    if (nFmtChar == '-')
        {
	pacTable ['-'] ^= 1;
	nFmtChar = (int) *pszFormat++;
        }
/*
 *  Collect the character set
 */
    while (nFmtChar)
        {
	if (nFmtChar == ']')
	    {
	    nFmtChar = (int) *pszFormat++;
	    break;
	    }
	pacTable [nFmtChar] ^= 1;
	nLower   = nFmtChar;
	nFmtChar = (int) *pszFormat++;
/*
 *  The sequence of a-z will define the range of characters between
 *  a and z, inclusive.
 */
	if (nFmtChar == '-')
	    {
	    nUpper = (int) *pszFormat;
	    if (nUpper >= nLower)
	        {
		pszFormat += 2;
		nFmtChar   = *pszFormat++;

		while (++nLower <= nUpper)
		    {
		    pacTable [nLower] ^= 1;
		    }
	        }
	    }
        }
/*
 *  Collect the string accordig to the table. Return the pointer to the
 *  termination character which is _always_ the previous character scanned.
 *  (By definition, we have gone one character too far in the scan.)
 */
    CollectString (1, CollectString_Table);
    return (&pszFormat [-1]);
    }

/***************************************************************************
 *
 * Function:     _input
 *
 * Description:
 *    Process the scanf / vscanf / sscanf functions. It will scan the input
 *    file (or character string) for the parameters based upon the formatting
 *    data kept in the format string.
 *
 * Entry:
 *    stream    - Pointer to the input FILE
 *    pszFormat - Pointer to the format string
 *    arglist   - Vector of input arguments
 *
 * Returns:
 *   The number of items processed or EOF to indicate that the end of the
 *   input file has been reached.
 */

int _input (FILE *stream, const char *pszFormat, va_list arglist)
    {
    int  nFmtChar;
    int  nInputChar;
    int  nMatchRslt;
/*
 *  Initialize the module local storage
 */
    fFoundEOF  = 0;            /* No EOF condition is pending         */
    fpInput    = stream;       /* The input stream file pointer       */
    argp       = arglist;      /* The list of output arguments        */
    nCharCount = 0;            /* Count of characters processed       */
    nItemCount = 0;            /* Count of items processed            */
    nPushBack  = 0;            /* No characters in pushback queue     */
    nMatchRslt = 0;            /* Result of the function              */
/*
 *  Collect the formatting information
 */
    nFmtChar = (int) *pszFormat++;
    while (nMatchRslt == 0 && nFmtChar)
        {
/*
 *  If the format is a space then skip blanks on the input and all
 *  blanks in the format string.
 */
        if (isspace (nFmtChar))
	    {
            SkipBlanks();
            do
	        {
                nFmtChar = (int) *pszFormat++;
	        }
            while (isspace (nFmtChar));
	    }
/*
 *  If this is a not a format string then it must match the input
 *  character sequence.
 */
        if (nFmtChar != '%')
	    {
	    nMatchRslt = MatchInput (nFmtChar);
	    if (nMatchRslt != 0)
	        {
		break;
	        }
	    nFmtChar = (int) *pszFormat++;
	    continue;
	    }
/*
 *  Match the single "%" character sequence.
 */
	nFmtChar = (int) *pszFormat++;
	if (nFmtChar == '%')
	    {
	    nMatchRslt = MatchInput (nFmtChar);
	    if (nMatchRslt != 0)
	        {
		break;
	        }
	    nFmtChar = (int) *pszFormat++;
	    continue;
	    }
/*
 *  Initialize to process the formatting information.
 */
	nArgSize         = 0;     /* Size of the input field     */
	nWidth           = 0;     /* Maximum with of the input   */
	fSuppressedStore = 0;     /* Do not suppress the storage */
/*
 *  If the character is "*" then suppress the storage of the next item.
 *  Do the scan only.
 */
	if (nFmtChar == '*')
	    {
	    fSuppressedStore = 1;
	    nFmtChar         = (int) *pszFormat++;
	    }
/*
 *  Fetch the field width
 */
	if (isdigit (nFmtChar))
	    {
	    do
	        {
		nWidth   *= 10;
		nWidth   += nFmtChar - '0';
		nFmtChar  = (int) *pszFormat++;
	        }
	    while (isdigit (nFmtChar));
/*
 *  If width is zero then skip the input data and simply advance the
 *  argument pointer.
 */
	    if (nWidth <= 0)
	        {
		nWidth = -1;
	        }
	    }
/*
 *  Scan a range of characters based upon a specific character set.
 */
	if (nFmtChar == '[')
	    {
	    pszFormat = ScanStringRange (pszFormat);
	    nFmtChar  = (int) *pszFormat++;
	    continue;
	    }
/*
 *  This is not a group of characters. Collect the storage modifier.
 */
	if (nFmtChar == 'h')
	    {
	    nArgSize = SHORT;
	    nFmtChar = (int) *pszFormat++;
	    }
	else
	    {
	    if (nFmtChar == 'l')
	        {
		nArgSize = LONG;
		nFmtChar = (int) *pszFormat++;
	        }
	    else
	        {
		if (nFmtChar == 'L')
		    {
		    nArgSize = LONGLONG;
		    nFmtChar = (int) *pszFormat++;
		    }
	        }
	    }
/*
 *  If there is no format modifier then stop processing at this point.
 */
	if (nFmtChar == '\0')
	    {
	    break;
	    }
/*
 *  Process the nul terminated string for the space bound keyword.
 */
	switch (nFmtChar)
	    {
	case 's':
	    SkipBlanks();
	    CollectString (1, CollectString_Space);
	    break;
/*
 *  Process a single character or a specific length of characters.
 */
	case 'c':
	    CollectString (0, CollectString_All);
	    break;
/*
 *  Process an integer input
 */
	case 'I':
	case 'i':
	    SkipBlanks();
	    nInputChar = NextFieldChar();
	    if (nInputChar == '0')
	        {
		nInputChar = NextFieldChar();
		if (nInputChar == 'x' || nInputChar == 'X')
		    {
		    CollectFixedPt (16);
		    }
		else
		    {
		    PushBack (nInputChar);
		    CollectFixedPt (8);
		    }
	        }
	    else
	        {
		PushBack (nInputChar);
		CollectFixedPt (10);
	        }
	    break;
/*
 *  Octal input
 */
	case 'O':
	case 'o':
	    SkipBlanks();
	    CollectFixedPt (8);
	    break;
/*
 *  Hexadecimal input
 */
	case 'X':
	case 'x':
	    SkipBlanks();
	    CollectFixedPt (16);
	    break;
/*
 *  Decimal input
 */
	case 'D':
	case 'd':
	    SkipBlanks();
	    CollectFixedPt (10);
	    break;
/*
 *  Character count processed
 */
	case 'n':
	    StoreFixedPt (nCharCount);
	    break;
/*
 *  Pointer
 */
	case 'p':
	    SkipBlanks();
	    CollectFixedPt (16);
	    break;
/*
 *  Floating point format
 */
	case 'e':
	case 'E':
	case 'f':
	case 'F':
	case 'g':
	case 'G':
	    SkipBlanks();
	    CollectFloatPnt();
	    break;
/*
 *  All others are strange.
 */
	default:
	    nMatchRslt = MatchInput (nFmtChar);
	    if (nMatchRslt != 0)
	        {
		break;
	        }
	    break;
	    }
	nFmtChar = (int) *pszFormat++;
        }
/*
 * This is the end of the conversion operation. Really return the character
 * to the file's unget buffer if one is pending.
 */
    if (nPushBack != 0)
        {
	nInputChar = NextInputChar();
	(void) ungetc (nInputChar, fpInput);
        }
/*
 *  If EOF is pending then return the EOF condition. Otherwise, return the
 *  character count as scanned.
 */
    if (nItemCount == 0 && fFoundEOF)
        {
	nItemCount = EOF;
        }
    return (nItemCount);
    }
