/*  INIFILE.C	12-Apr-99 11:31:01 by Kevin D. Quitt

    Initialization file utility routines.

19-Mar-03 14:32, [ROCKYROAD]d:\isiarm\Sample Utilities\InputDemo\inifile.c, Edit by kdq
    Hacked even more for ScanVue touch demo.

15-May-00 20:29, [KDQ]\isiarm\users\dev\psosarm_pwe\apps\scanvue\inifile.c, Edit by KDQ
    Hacked to pieces for the NetARM scanvue.
*/
#define	DUMPINIENABLED	(0)

#define	FALSE	(0)
#define	TRUE	(1)

#if DUMPINIENABLED
#include    <stdio.h>
#endif
#include    <string.h>
#include    <ctype.h>

#include    "strutil.h"
#include    "inifile.h"

static char templine[ TEMPLINESIZE ];	    /* Current input line      	    */

static size_t	linelen;		    /* Length of active portion	    */
static short	reuseLine;		    /* Boolean			    */

static char    *lastIniString;

static char    *iniFile;
static size_t	iniFileRemains;

#if DUMPINIENABLED

static void dumpTVPairs( SECTION *Section )
  {
    int	    tv;
    TVPAIR *TVPairs = Section->TVPair;
    int	    count   = Section->TVCount;

    if ( !TVPairs  ||  (count  ==  0))
      {
	printf( "  No TVPairs\n" );
	return;
      }

    for ( tv = 0; tv < count; tv++ )
      {
	printf( "  TVPair %02d @%08X\n",
		tv, &TVPairs[ tv ] );
	printf( "   Token @%08X: %s\n",
		TVPairs[ tv ].Token, strVal( TVPairs[ tv ].Token ));
	printf( "   Value @%08X: %s\n",
		TVPairs[ tv ].Value, strVal( TVPairs[ tv ].Value ));
      }
  }

static void dumpSections( INIFILE *IFP )
  {
    int	s;
    int	count	= IFP->SectionCount;

    if ( count  ==  0 )
      {
	printf( "\nNo Sections\n" );
	return;
      }

    for ( s = 0; s < count; s++ )
      {
	printf( "Section %2d at %08X\n", s, &IFP->Section[ s ]		    );
	printf( " Name at %08X is %s\n", IFP->Section[ s ].Name,
		strVal( IFP->Section[ s ].Name ));
	printf( " TVPairs %2d @%08X\n",  IFP->Section[ s ].TVPair,
		IFP->Section[ s ].TVCount);
	dumpTVPairs( &IFP->Section[ s ]);
      }
  }

static void dumpIni( INIFILE *IFP )
  {
    printf( "\n\n\n" );
    printf( "IniFile:     @%08X\n",    IFP				     );
    printf( "Sections: %2d @%08X\n",   IFP->SectionCount, &IFP->Section[ 0 ]);
    printf( "TVPairs:  %2d @%08X\n",   IFP->TVCount,       IFP->TVPair	     );
    printf( "Next TVPair: @%08X\n",    IFP->NextTVPair			     );
    printf( "String top:  @%08X\n",    IFP->StringTop			     );
    dumpSections( IFP );
  }
#endif


/*  Return a pointer to the named section.
*/
SECTION	   *getINISection( INIFILE *IFP, char *name )
  {
    int	s;

    if ( name )
      {
	for ( s = 0; s < IFP->SectionCount; s++ )
	  {
	    if ( !stricmp( name, IFP->Section[ s ].Name ))
		return  &IFP->Section[ s ];
	  }
      }

    return  NULL;
  }


/*  Get the value for the named token within the named section.  Restrict
    matches to IDs provided.  Return the first match with proper ID, or
    the first match without ID if no match is found.  Return NULL if all
    matching tokens have non-matching IDs.
*/
char   *getINIValue( INIFILE *IFP, char *sectionName, char *token )
  {
    char       *noIDmatch   = NULL;
    SECTION    *theSect	    = NULL;
    TVPAIR     *tvp	    = NULL;
    short	scan;

    theSect = getINISection( IFP, sectionName );
    if ( !theSect )
	return	NULL;
    tvp	    = theSect->TVPair;


    for ( scan = 0; scan < theSect->TVCount; scan++ )
      {
	if ( stricmp( token, strVal( tvp[ scan ].Token )))
	    continue;
      }

    return  noIDmatch;
  }


static size_t	adjustedCount( size_t count )
  {
    size_t  toreturn;

    if ( iniFileRemains  >  count )
      {
	toreturn	= count;
	iniFileRemains	-= count;
      }
    else
      {
	toreturn	= iniFileRemains;
	iniFileRemains	= 0;
      }

    return  toreturn;
  }


/*  Migrated from reading a file.  Read the next line from the buffer and
    start to process it.
*/
static char   *readline( void )
  {
    char    *pline  = NULL;		/* Points into line		    */
    size_t  count;			// Number of characters to copy

    if ( !iniFileRemains  ||  !iniFile )
      {
	iniFileRemains	= 0;		/* Stewardship			    */
	iniFile		= NULL;
	return	pline;			/* No more data			    */
      }

    templine[ 0 ]   = '\0';		/* Bullet-proofing.		    */

    count   = strcspn( iniFile, "\n\r");/* Count chars on the line	    */
    count   = adjustedCount( count );

    if ( count )			/* Anything there?		    */
      {
	strncpy( templine, iniFile, count );
	templine[ count ]   = 0;

	iniFile += count;		/* Move along			    */
	count	= strspn( iniFile, "\n\r" );
	count   = adjustedCount( count );
	iniFile	+= count;		/* On to the next line		    */


/*  Skip leading whitespace and remove comments and trailing whitespace.
*/
	pline   = templine + strspn( templine, " \t\n\r\f" );
	stripComment( pline );
	stripTerminalWhitespace( pline );
	linelen = strlen( pline );
      }

    return  pline;
  }



/*  Read the file and return a line that is not zero length, all whitespace,
    or is a comment (starting with "#" or "'".  Return NULL if there isn't
    anything there for any reason.
*/
static char *getIniLine( void )
  {
    static char	   *bline   = NULL;
    char    foo	    = '\0';
    char   *tline   = &foo;

    if ( reuseLine )
      {
	reuseLine   = FALSE;
	return	bline;
      }
    else
	bline	= NULL;

    while ( tline  &&  !*tline )
	tline	= readline();		/* crud stripped		    */

    bline   = tline;
    return  bline;
  }


/*  Place a string in the INI's string buffer and return the pointer.
*/
static char    *addIniString( INIFILE *IFP, char *tosave, size_t slen )
  {
    char   *where;

    if ( slen  ==  0 )
	slen	= strlen( tosave );

    where   = IFP->StringTop - slen - 1;
    if ( where  <  (char *)IFP->NextTVPair )
	return	NULL;

    IFP->StringTop	= where;
    lastIniString	= where;
    strncpy( where, tosave, slen );

    return  where;
  }


/*  Allocate a(nother) SECTION for the INIFILE
*/
static short	addSection( INIFILE *IFP )
  {
    if ( IFP->SectionCount  >=  MAXSECTS - 1 )
	return	FALSE;

    IFP->SectionCount++;

    return  TRUE;
  }


/*  Find the beginning of the (next) section, and fill in the structure.
*/
static short	startSection( INIFILE *IFP )
  {
    char       *esect   = "";
    char       *pline;
    SECTION    *sect;

    pline   = getIniLine();
    if ( !pline )
	return	FALSE;

    if ( *(pline++)  ==  '[' )		/* Section start?		    */
      {


/*  Section name found.  Find the closing bracket; if there isn't one, use
    the end of the line.
*/
	esect	= strchr( pline, ']' );

	if ( esect )			/* Found the terminator?	    */
	    *esect  = '\0';		/* Yep.  Stomp it		    */
      }
    else
	reuseLine   = TRUE;		/* A TV pair?  No section name	    */

    if ( !addSection( IFP ))
	return	FALSE;

    sect	    = &IFP->Section[ IFP->SectionCount - 1 ];
    sect->Name	    = addIniString( IFP, pline, 0 );

    if ( !sect->Name )
      {
	IFP->SectionCount--;
	return	FALSE;
      }

    return  TRUE;
  }



/*  Allocate a(nother) TVPAIR for the SECTION
*/
static short	addTVPair( INIFILE *IFP )
  {
    char       *newBot;
    SECTION    *Sect;
    TVPAIR     *newTV;

/*  Determine position of new TVPair, and push up the bottom of the
    strings.  If they cross, we don't add the pair.
*/
    newTV   = IFP->NextTVPair;
    newBot  = (char *)newTV + 1;

    if ( newBot  >=  IFP->StringTop )
	return	FALSE;

    IFP->TVCount++;			/* Accounting			    */
    IFP->NextTVPair++;


/*  If this is the first TVPair for the section, point the section at it.
    In any case, bump the count for this section.
*/
    Sect = &IFP->Section[ IFP->SectionCount - 1 ];
    if ( Sect->TVCount  ==  0 )
	Sect->TVPair	= newTV;
    Sect->TVCount++;

    return  TRUE;
  }


static void	removeTVPair( INIFILE *IFP )
  {
    SECTION    *Sect;

    if ( IFP->TVCount  >  0 )
      {
	IFP->NextTVPair--;
	IFP->TVCount--;
      }

    Sect = &IFP->Section[ IFP->SectionCount - 1 ];
    if ( --Sect->TVCount  <=  0 )
      {
	Sect->TVPair	= NULL;
	Sect->TVCount	= 0;
      }
  }


/*  Load the Token/Value pairs in this section
*/
static short	loadSection( INIFILE *IFP )
  {
    char       *pline;
    char       *token, *value;
    TVPAIR     *tvp;
    SECTION    *Sect	= &IFP->Section[ IFP->SectionCount - 1 ];


/*  Scan until we find an open square bracket, which indicates the start of
    the next section.
*/
    while ((pline = getIniLine())  !=  NULL )
      {
	if ( *pline  ==  '[' )		/* Next section?		    */
	  {
	    reuseLine	= TRUE;		/* Somebody else will want this	    */
	    return  TRUE;
	  }

/*  We are looking at one of three possibilitie: a token/value pair, a
    token/value pair, or garbage.  We make room for it  because it can't
    hurt, then decide what to do with it. 
*/
	if ( !addTVPair( IFP ))
	    continue;
	tvp = &Sect->TVPair[ Sect->TVCount - 1 ];


/*  Check for token terminator.
*/
	if ( !splitTVPair( pline, &token, &value ))
	  {
	    removeTVPair( IFP );
	    continue;
	  }

	if ( token )			/* Token can be null		    */
	    tvp->Token	= addIniString( IFP, token, 0);

	if ( value )
	    tvp->Value  = addIniString( IFP, value, 0 );
      }

    return  TRUE;
  }

/*  Load the specified INI file and fill in the INIFILE structure.
*/
short	LoadIniFile( INIFILE *IFP, char *buffer, size_t size )
  {
    if ( !IFP  ||  !buffer  ||  !size )
	return	FALSE;

    iniFile	    = buffer;		/* Record buffer active location    */
    iniFileRemains  = size;		/* and size			    */

    memset( IFP, 0, sizeof *IFP );

    IFP->TVPair	    = (TVPAIR *)&IFP->Workspace[ 0 ];
    IFP->NextTVPair = IFP->TVPair;
    IFP->StringTop  = IFP->Workspace + sizeof IFP->Workspace;

    while ( iniFileRemains )
      {
	if ( !startSection( IFP ))
	    return  FALSE;

	if ( !loadSection(  IFP ))
	    return  FALSE;
      }

#   if DUMPINIENABLED
	dumpIni( IFP );
#endif


    return  TRUE;
  }
