
/**
* module	DBC\CONFIG
*
* purpose	Used to access "CONFIG.SYS-format" and "WIN.INI-format" files.
*		Similar to Windows Initialization File functions.
*		The following file format is processed:
*
*			; comment
*			[SectionName]	or  [SectionName] ; comment
*			KeyName=string	or  KeyName=string ; comment
*			KeyName=	or  KeyName= ; null string
*			       =string	or	   =string ; blank KeyName
*			KeyName 	or  KeyName ; same as KeyName=
*
*		Comments must be on separate lines or else the ';' must be
*		preceded by a blank (to allow ';' to occur in strings).
*		Blanks are not allowed between "SectionName" and the brackets.
*		"SectionName" and "KeyName" are not case-dependent, so the
*		strings may contain any combination of upper and lower case.
*		If there is no equal sign, the entire line is assumed the key.
*		The existence of an equal sign (after the removal of comments)
*		determines that a string exists; otherwise it is assumed Null.
*		Blanks around "KeyName" and "string" are ignored.  A null or
*		blank KeyName (when an equal sign is present) is assumed to be
*		a single blank.  Blank lines are allowed anywhere and ignored.
*		SectionNames must be unique, since only the first one is used.
*		KeyNames should be unique, since all are processed similarly.
*
* logic 	The file is processed sequentially.  Each line is read, detabbed
*		and parsed and then a "call-back" function is called with the
*		resulting line components determined.  The components are:
*
*			FilePre 		file line (including comments)
*
*		and (if available)
*
*			FileKey -> FilePost	left of the equal sign
*						(a blank if there is no key)
*		and	FileString -> FilePost	right of the equal sign
*						(Null if there is no string)
*
*		The call-back function returns a code to indicate continued
*		scanning.  The parser returns if either the end-of-file is
*		reached or the call-back function ended the parsing.
*
* cautions	Argument checking is not performed.
*
* includes	<dbc/config.h>
*
* copyright	(c) Fran Finnegan Associates 1986, 1987
* copyright	(c) 1988-92 Finnegan O'Malley & Company Inc.
**/

#ifndef LINT_ARGS
#define LINT_ARGS
#endif
	#include <io.h>
	#include <malloc.h>
//	#include <stdio.h>
	#include <stdlib.h>
//	#include <string.h>

	#include "ascii.i"
//	#include "const.i"
	#include "filespec.i"
//	#include "string.i"
//	#include "config.i"

#define NAME_SIZE	64	/* for FileNames, SectionNames and KeyNames */
#define LINE_SIZE	256	/* for file lines */

#define ABOUT_TO_BEGIN	-1	/* to call-back:  about to begin reading file */
#define NOT_IN_SECTION	NO	/* to call-back:  not in Section requested */
#define ARE_IN_SECTION	YES	/* to call-back:  are in Section requested */
#define END_OF_PROCESS	2	/* to call-back:  end-of-process was reached */

#define CONTINUE	1	/* to ParseFile:  continue reading file */
#define STOP		2	/* to ParseFile:  stop reading file */

#define ACCESS_EXISTS	0	/* "access" existence-only mode */
#define ACCESS_UNLINK	6	/* "access" read-and-write mode */

	int		gbConfigCloseFileOnExit = NO;
	int		gbConfigDeleteBakOnExit = YES;										static char *_="\x20\x28\x63\x29 \x31\x39\x39\x32 \x46\x69\x6E\x6E\x65\x67\x61\x6E \x4F\x27\x4D\x61\x6C\x6C\x65\x79\x2E\x20";

static	int		mfnGetConfigString(int);
static	int		mfnWriteConfigString(int);
static	int		mfnGetConfigSection(int);
static	int		mfnGetConfigKeys(int);

static	void		CloseFile(void);
static	void		ParseFile(char *, char *, int (*)(int));
static	void		ParseSection(char *, char *);
static	void		ParseKey(char *, char *);
static	char		*MemoryGetString(char *, int, char far **);

static	char		far *mlpmemConfig;	/* memory file in process */
static	FILE		*mpfileConfig;		/* stream file in process */
static	char		*mpszUserDefault;	/* user supplied */
static	char		*mpszUserReturned;	/* user supplied */
static	unsigned int	  muiUserSize;		/* user supplied */
static	char		*mpszUserString;	/* user supplied */
static	char		*mpszFileKey;		/* pointer to mszFilePost */
static	char		*mpszFileString;	/* pointer to mszFilePost */
static	int		   miReturn;		/* return code */
static	int		   mbWriteRequest;	/* WriteConfig... request */
static	char	mszUserFile   [NAME_SIZE],	/* "d:\\path\\filename.ext" */
		mszUserSection[NAME_SIZE],	/* "[SectionName]" or "" */
		mszUserKey    [NAME_SIZE],	/* "KeyName" or " " */
		mszFilePre    [LINE_SIZE],	/* pre-parsing line */
		mszFilePost   [LINE_SIZE];	/* post-parsing line */

/*p*/

/**
* function	GetConfigInt
*
* purpose	Similar to Windows "GetProfileInt".
*
* logic 	Calls "GetConfigString".
*
* cautions	Unless the file contains a valid int, the default is returned.
*
* usage 	iKeyValue = GetConfigInt(pszFilespec, pszSection, pszKey,
*			iDefault);
*
* arguments	pszFilespec	"d:\\path\\filename.ext" format.
*				pszFilespec must not be NULL.
*		pszSection	"SectionName" of "[SectionName]".
*				pszSection may be NULL, implying "".
*		pszKey		"KeyName" of "KeyName=string".
*				pszKey may be NULL, implying " ".
*		iDefault	Default int if the SectionName/KeyName
*				combination does not exist or is Null.
*
* returns	iKeyValue = int value of the key string.
*
* modifies	no externals.
*
* examples	iDOSbuffers = GetConfigInt("C:\\CONFIG.SYS", NULL, "buffers",
*			-1);
*
* copyright	(c) Fran Finnegan Associates 1986, 1987
**/

extern	int		GetMemoryInt(lpmemConfig, pszSection, pszKey, iDefault)

	char		far *lpmemConfig,
			*pszSection,
			*pszKey;
	int		iDefault;
{
auto	int		iReturn;

CloseFile();
mlpmemConfig = lpmemConfig;
iReturn = GetConfigInt(NULL, pszSection, pszKey, iDefault);
mlpmemConfig = (char far *)NULL;
return (iReturn);
}

extern	int		GetConfigInt(pszFilespec, pszSection, pszKey, iDefault)

	char		*pszFilespec,
			*pszSection,
			*pszKey;
	int		iDefault;
{
auto	char		szReturned[12]; 	/* [12] should be sufficient */

return ((GetConfigString(pszFilespec, pszSection, pszKey, "", szReturned,
		sizeof(szReturned)) and
	strspn(szReturned, "+-0123456789"))?  atoi(szReturned):  iDefault);
}

/*p*/

/**
* function	GetConfigString
*
* purpose	Similar to Windows "GetProfileString".
*
* logic 	See "arguments" below.
*
* cautions	The "Returned" buffer must be at least 2 bytes long.  It should
*		be one byte longer than the longest expected "string".
*
* usage 	iLength = GetConfigString(pszFilespec, pszSection, pszKey,
*			pszDefault, pszReturned, uisize);
*
* arguments	pszFilespec	"d:\\path\\filename.ext" format.
*				pszFilespec must not be NULL.
*		pszSection	"SectionName" of "[SectionName]".
*				pszSection may be NULL, implying "".
*				Blanks around pszSection are ignored.
*				Brackets are not included, but added herein.
*				If pszSection == "":
*					SectionNames in the file are ignored.
*		pszKey		"KeyName" of "KeyName=string".
*				pszKey may be NULL, implying " ".
*				Blanks around pszKey are ignored.
*				If pszKey == "":
*					A null or blank KeyName is searched for.
*		pszDefault	Default string if the SectionName/KeyName
*				combination does not exist or is Null.
*				If pszDefault == NULL:
*					The KeyName itself is the default.
*				If pszDefault == "":
*					The Null string is the default.
*				The default is used if either the KeyName does
*				not exist or the Key string exists but is Null.
*		pszReturned	Returned-string buffer (at least 2 bytes).
*		uisize		Returned-string buffer size.
*
* returns	iLength = int length of returned string.  0 indicates Null ("").
*
* modifies	no externals.
*
* examples	auto	char	szDOSshell[64];
*
*		iDOSshell = GetConfigString("C:\\CONFIG.SYS", NULL, "shell",
*			"C:\\COMMAND.COM", szDOSshell, sizeof(szDOSshell));
*
* copyright	(c) Fran Finnegan Associates 1986, 1987
**/

extern	int		GetMemoryString(lpmemConfig, pszSection, pszKey,
				pszDefault, pszReturned, uisize)

	char		far *lpmemConfig,
			*pszSection,
			*pszKey,
			*pszDefault,
			*pszReturned;
	unsigned int	uisize;
{
auto	int		iReturn;

CloseFile();
mlpmemConfig = lpmemConfig;
iReturn = GetConfigString(NULL, pszSection, pszKey, pszDefault, pszReturned,
	uisize);
mlpmemConfig = (char far *)NULL;
return (iReturn);
}

extern	int		GetConfigString(pszFilespec, pszSection, pszKey,
				pszDefault, pszReturned, uisize)

	char		*pszFilespec,
			*pszSection,
			*pszKey,
			*pszDefault,
			*pszReturned;
	unsigned int	uisize;
{
ParseKey(mszUserKey, pszKey);					/* Key	    */
mpszUserDefault = pszDefault;					/* Default  */
mpszUserReturned = pszReturned; 				/* Returned */
muiUserSize = uisize;						/* Size     */
ParseFile(pszFilespec, pszSection, mfnGetConfigString);
return (miReturn);
}

/*p*/

static	int		mfnGetConfigString(iCase)

	int		iCase;
{
static	FILE		*pfileConfig;		/* stream file last processed */
static	int		bInSection = NO,
			bKeyFound = NO,
			iFile_cnt = -1;
static	char		szUserFile   [NAME_SIZE],
			szUserSection[NAME_SIZE];

register char		*pC;

switch (iCase) {

    case ABOUT_TO_BEGIN:
	bKeyFound = NO;
	*mpszUserReturned = Null;
	if (muiUserSize < 2)	/* buffer size must be at least 2 bytes */
	    return (STOP);
	memset(mpszUserReturned, Null, muiUserSize--); /* -- insures a string */
	if (bInSection and
		pfileConfig and
		pfileConfig == mpfileConfig and
		iFile_cnt == mpfileConfig->_cnt and
		no ferror(mpfileConfig) and
		IsStrEquiv(szUserFile	, mszUserFile	) and
		IsStrEquiv(szUserSection, mszUserSection) and
		fgets(mszFilePre, LINE_SIZE, mpfileConfig)) {	/* has '\n' */
	    StrTrunc(mszFilePre, " \t\r\n");
	//  StrDetab(mszFilePre);
	    if (!IsStrNull(mszFilePre) and
		    *mszFilePre != ';') {
		if (pC = strstr(mszFilePre, " ;"))
		    *pC = Null; 			/* remove comments */
		StrCrop(mszFilePre, " ");               /* now "Key = String" */
		if (!IsStrNull(mszFilePre)) {	      /* or "Key=" or "Key" */
		    if (pC = strchr(mszFilePre, '=')) { /* if an equal sign */
			*pC++ = Null;			/* end Key */
			pC = StrSkip(pC, " ");          /* skip to String */
			StrTrunc(mszFilePre, " ");      /* truncate Key */
			}
		    if (IsStrEquiv(mszFilePre, mszUserKey)) {
			bKeyFound = YES;
			if (pC)
			    strncpy(mpszUserReturned, pC, muiUserSize);
			mfnGetConfigString(END_OF_PROCESS);	/* recursion! */
			if (gbConfigCloseFileOnExit)
			    CloseFile();
			return (STOP);
			}
		    }
		}
	    }
	strcpy(szUserFile   , mszUserFile   );
	strcpy(szUserSection, mszUserSection);
	bInSection = NO;
	break;

    case ARE_IN_SECTION:
	bInSection = YES;
	if (mpszFileKey and IsStrEquiv(mpszFileKey, mszUserKey)) {
	    bKeyFound = YES;
	    strncpy(mpszUserReturned, mpszFileString, muiUserSize);
	    if (IsStrNULL(mpszUserDefault))
		mpszUserDefault = mpszFileKey;
	    return (STOP);	/* Key was found */
	    }
	break;

    case NOT_IN_SECTION:
	if (bInSection) {
	    bInSection = NO;
	    return (STOP);	/* was in, but am now not */
	    }
	break;

    case END_OF_PROCESS:
	if (bKeyFound)
	    while (!IsStrNull(mpszUserReturned)) {
		pC = StrNull(mpszUserReturned) - 1;
		if (*pC != '\\')
		    break;
		*pC = '\0';
		if (no fgets(mszFilePre, LINE_SIZE, mpfileConfig))
		    break;					/* has '\n' */
		StrTrunc(mszFilePre, " \t\r\n");
	    //	StrDetab(mszFilePre);
		StrLeft(mszFilePre, " ");
		strncpy(pC, mszFilePre, muiUserSize - strlen(mpszUserReturned));
		}
	if (IsStrNull(mpszUserReturned))
	    strncpy(mpszUserReturned, (mpszUserDefault?  mpszUserDefault:
		    mszUserKey), muiUserSize);
	miReturn = strlen(mpszUserReturned);
	iFile_cnt = (pfileConfig = mpfileConfig)?  mpfileConfig->_cnt:	-1;
	break;		 /* the '=' in the above line is OK */
    }
return (CONTINUE);
}

/*p*/

/**
* function	WriteConfigString
*
* purpose	Similar to Windows "WriteProfileString".
*		Writes "KeyName=string" in the requested "SectionName"
*		(replacing an existing KeyName if it already exists).
*
* logic 	See "arguments" below.  The original file (if it exists) is
*		renamed to "*.BAK" after the new file is written.
*
* cautions	none.
*
* usage 	iSuccess = WriteConfigString(pszFilespec, pszSection, pszKey,
*			pszString);
*
* arguments	pszFilespec	"d:\\path\\filename.ext" format.
*				pszFilespec must not be NULL.
*		pszSection	"SectionName" of "[SectionName]".
*				pszSection may be NULL, implying "".
*				Blanks around pszSection are ignored.
*				Brackets are not included, but added herein.
*				If pszSection == "":
*					SectionNames in the file are ignored.
*		pszKey		"KeyName" of "KeyName=string".
*				pszKey may be NULL, implying " ".
*				Blanks around pszKey are ignored.
*				If pszKey == "":
*					A blank KeyName is written.
*		pszString	"string" of "KeyName=string".
*				If pszString is NULL, the old "KeyName=string"
*				is removed.  A Null ("") pszString is permitted.
*
* returns	iSuccess = TRUE if successful, FALSE if not.
*
* modifies	no externals.
*
* examples	iSuccess = WriteConfigString("C:\\CONFIG.SYS", NULL, "files",
*			"32");
*
* copyright	(c) Fran Finnegan Associates 1986, 1987
**/

extern	int		WriteConfigString(pszFilespec, pszSection, pszKey,
				pszString)

	char		*pszFilespec,
			*pszSection,
			*pszKey,
			*pszString;
{
mbWriteRequest = YES;
ParseKey(mszUserKey, pszKey);					/* Key	    */
mpszUserString = pszString;					/* String   */
ParseFile(pszFilespec, pszSection, mfnWriteConfigString);
mbWriteRequest = NO;
return (miReturn);
}

/*p*/

static	int		mfnWriteConfigString(iCase)

	int		iCase;
{
static	FILE		*pfileTMP;
static	int		bWritten;
static	char		*pszCrLf,
			szCrLf[] = "\n",
			szBAK[] = "BAK",
			szTMP[] = "TMP",
			szFILEformat[] = "%s\n";

auto	char		szFilespec[FILESPEC	 ],
			szDrive   [FILESPEC_DRIVE],
			szPath	  [FILESPEC_PATH ],
			szName	  [FILESPEC_NAME ],
			szExt	  [FILESPEC_EXT  ];

switch (iCase) {

    case ABOUT_TO_BEGIN:
	miReturn = NO;		/* used as a Section indicator */
	if (*mszUserFile == ' ')
	    return (STOP);	/* old Filespec can not be a blank (NULL) */
	ParseFilespec(mszUserFile, szDrive, szPath, szName, szExt);
	if (IsStrEquiv(szExt, szBAK) or IsStrEquiv(szExt, szTMP))
	    return (STOP);	/* old Filespec can not be *.BAK or *.TMP */
	if (!access(mszUserFile, ACCESS_EXISTS)) {	/* if exist *.old */
	    MakeFilespec(szFilespec, szDrive, szPath, szName, szBAK);
	    if (!access(szFilespec, ACCESS_EXISTS))	/* if exist *.BAK */
		if (access(szFilespec, ACCESS_UNLINK) or unlink(szFilespec))
		    return (STOP);		/* could not delete *.BAK */
	    }
	MakeFilespec(szFilespec, szDrive, szPath, szName, szTMP);
	if (!access(szFilespec, ACCESS_EXISTS) or	/* if exist *.TMP */
		no (pfileTMP = fopen(szFilespec, "wt")))  /* no new *.TMP */
	    return (STOP);			  /* could not open *.TMP */
	pszCrLf = "";
	bWritten = mpszUserString == NULL;  /* assume written for removal */
	break;

    case ARE_IN_SECTION:
	if (IsStrNull(mszFilePre))
	    break;
	if (*mszFilePost == '[') {
	    fprintf(pfileTMP, pszCrLf);
	    pszCrLf = szCrLf;
	    }
	miReturn = YES; 	/* used as a Section indicator */
	if (mpszFileKey and IsStrEquiv(mpszFileKey, mszUserKey)) { /* a match */
	    if (!IsStrNULL(mpszUserString)) {			 /* replace */
		strcpy(StrSkip(mszFilePre, " "),
			&mszUserKey[*mszUserKey == ' ']);
		strcat(mszFilePre, (*mszUserKey != ' ' and *mszFilePre == ' ')?
			" = ":  "=");
		strcat(mszFilePre, mpszUserString);
		}
	    bWritten++;  /* may be written here (in Section) multiple times */
	    }
	if (!(IsStrNULL(mpszUserString) and /* !(string NULL & it is a match) */
		mpszFileKey and IsStrEquiv(mpszFileKey, mszUserKey)))
	    fprintf(pfileTMP, szFILEformat, /*StrEntab()*/ mszFilePre); /* old/new */
	break;

    case NOT_IN_SECTION:
	if (IsStrNull(mszFilePre))
	    break;
	if (miReturn and	/* used as a Section indicator */
		not bWritten) { 				    /* append */
	    fprintf(pfileTMP, "%s=%s\n", &mszUserKey[*mszUserKey == ' '],
		    mpszUserString);				    /* add */
	    bWritten = YES;
	    }
	if (*mszFilePost == '[') {
	    fprintf(pfileTMP, pszCrLf);
	    pszCrLf = szCrLf;
	    }
	fprintf(pfileTMP, szFILEformat, /*StrEntab()*/ mszFilePre); /* old */
	break;

    case END_OF_PROCESS:
	if (no miReturn and	/* used as a Section indicator */
		!IsStrNull(mszUserSection)) {  /* never NULL */
	    fprintf(pfileTMP, pszCrLf);
	    fprintf(pfileTMP, szFILEformat, mszUserSection);	    /* add */
	    }
	if (not bWritten) {
	    strcpy(mszFilePre, &mszUserKey[*mszUserKey == ' ']);
	    strcat(mszFilePre, "=");
	    strcat(mszFilePre, mpszUserString);
	    fprintf(pfileTMP, szFILEformat,
		    /*StrEntab()*/ mszFilePre); 		    /* add */
	    }
	fprintf(pfileTMP, "%c", A_EOF);                             /* EOF */
	fclose(pfileTMP);
	ParseFilespec(mszUserFile, szDrive, szPath, szName, szExt);
	MakeFilespec(szFilespec, szDrive, szPath, szName, szBAK);
	rename(mszUserFile, szFilespec);	/* rename *.old *.BAK */
	if (gbConfigDeleteBakOnExit)
	    unlink(szFilespec);
	MakeFilespec(szFilespec, szDrive, szPath, szName, szTMP);
	rename(szFilespec, mszUserFile);	/* rename *.TMP *.old */
	miReturn = YES; 			/* OK */
	break;
    }
return (CONTINUE);
}

/*p*/

/**
* function	GetConfigSection
*
* purpose	Gets an entire Section.  Each line is placed in the "Returned"
*		buffer and terminated with a CR/LF (to be compatible with the
*		Windows "DrawText" function).  The buffer is Null-terminated.
*
* logic 	See "arguments" below.
*
* cautions	The "Returned" buffer must be at least 2 bytes long.  It should
*		be long enough to accomodate the longest expected Section.
*
* usage 	iLines = GetConfigSection(pszFilespec, pszSection, pszReturned,
*				  uiSize);
*
* arguments	pszFilespec	"d:\\path\\filename.ext" format.
*				pszFilespec must not be NULL.
*		pszSection	"SectionName" of "[SectionName]".
*				pszSection must be supplied and not be NULL.
*				Blanks around pszSection are ignored.
*				Brackets are not included, but added herein.
*		pszReturned	Returned-string buffer (at least 2 bytes).
*		uiSize		Returned-string buffer size.
*
* returns	iLines = number of lines found.  0 indicates that the Section
*				was not found or had no lines.
*
* modifies	no externals.
*
* examples	auto	char	szWinIniColors[512];
*
*		iWinIniColors = GetConfigSection("C:\\WIN\\SYS\\WIN.INI",
*			"Colors", szWinIniColors, sizeof(szWinIniColors));
*
* copyright	(c) Fran Finnegan Associates 1986, 1987
**/

extern	int		GetMemorySection(lpmemConfig, pszSection, pszReturned,
				uiSize)

	char		far *lpmemConfig,
			*pszSection,
			*pszReturned;
	unsigned int	uiSize;
{
auto	int		iReturn;

CloseFile();
mlpmemConfig = lpmemConfig;
iReturn = GetConfigSection(NULL, pszSection, pszReturned, uiSize);
mlpmemConfig = (char far *)NULL;
return (iReturn);
}

extern	int		GetConfigSection(pszFilespec, pszSection, pszReturned,
				uiSize)

	char		*pszFilespec,
			*pszSection,
			*pszReturned;
	unsigned int	uiSize;
{
mpszUserReturned = pszReturned; 				/* Returned */
muiUserSize = uiSize;						/* Size     */
ParseFile(pszFilespec, pszSection, mfnGetConfigSection);
return (miReturn);
}

/*p*/

static	int		mfnGetConfigSection(iCase)

	int		iCase;
{
register int		iLength;

auto	char		*pC;

switch (iCase) {

    case ABOUT_TO_BEGIN:
	miReturn = 0;		/* used as a line counter */
	*mpszUserReturned = Null;
	if (muiUserSize < 2 or IsStrNull(mszUserSection))
	    return (STOP);
	memset(mpszUserReturned, Null, muiUserSize--);
	break;

    case ARE_IN_SECTION:
	if (*mszFilePre != ';' and      /* ignore comment */
		miReturn++) {	/* used as a line counter */
	    if (pC = strstr(mszFilePre, " ;")) {
		*pC = Null;		/* remove comment */
		StrTrunc(mszFilePre, " ");
		}
	    iLength = strlen(strcat(mszFilePre, "\r\n"));
	    if (iLength > muiUserSize)
		mszFilePre[iLength = muiUserSize] = Null;
	    strcpy(mpszUserReturned, mszFilePre);
	    mpszUserReturned += iLength;  /* effectively concats */
	    if ((muiUserSize -= iLength) == 0)
		return (STOP);	/* no more room */
	    }
	break;

    case NOT_IN_SECTION:
	if (miReturn)		/* used as a line counter */
	    return (STOP);	/* was in, but am now not */
	break;

    case END_OF_PROCESS:
	if (miReturn)		/* was in */
	    miReturn--; 	/* ignore "[Section]" line */
	break;
    }
return (CONTINUE);
}

/*p*/

/**
* function	GetConfigKeys
*
* purpose	Gets a set of Keys.  Each key is placed in the "Returned"
*		buffer and terminated with a CR/LF (to be compatible with the
*		Windows "DrawText" function).  The buffer is Null-terminated.
*
* logic 	See "arguments" below.
*
* cautions	The "Returned" buffer must be at least 2 bytes long.  It should
*		be long enough to accomodate the longest expected set of Keys.
*
* usage 	iKeys = GetConfigKeys(pszFilespec, pszSection, pszReturned,
*			uiSize);
*
* arguments	pszFilespec	"d:\\path\\filename.ext" format.
*				pszFilespec must not be NULL.
*		pszSection	"SectionName" of "[SectionName]".
*				pszSection may be NULL, implying "".
*				Blanks around pszSection are ignored.
*				Brackets are not included, but added herein.
*		pszReturned	Returned-string buffer (at least 2 bytes).
*		uiSize		Returned-string buffer size.
*
* returns	iKeys = number of keys found.  0 indicates that the Section
*				was not found or no keys were found.
*
* modifies	no externals.
*
* examples	auto	char	szWinIniFonts[1 K];
*
*		iWinIniFonts = GetConfigKeys("C:\\WIN\\SYS\\WIN.INI",
*			"Fonts", szWinIniFonts, sizeof(szWinIniFonts));
*
* copyright	(c) Fran Finnegan Associates 1986, 1987
**/

extern	int		GetMemoryKeys(lpmemConfig, pszSection, pszReturned,
				uiSize)

	char		far *lpmemConfig,
			*pszSection,
			*pszReturned;
	unsigned int	uiSize;
{
auto	int		iReturn;

CloseFile();
mlpmemConfig = lpmemConfig;
iReturn = GetConfigKeys(NULL, pszSection, pszReturned, uiSize);
mlpmemConfig = (char far *)NULL;
return (iReturn);
}

extern	int		GetConfigKeys(pszFilespec, pszSection, pszReturned,
				uiSize)

	char		*pszFilespec,
			*pszSection,
			*pszReturned;
	unsigned int	uiSize;
{
mpszUserReturned = pszReturned; 				/* Returned */
muiUserSize = uiSize;						/* Size     */
ParseFile(pszFilespec, pszSection, mfnGetConfigKeys);
return (miReturn);
}

/*p*/

static	int		mfnGetConfigKeys(iCase)

	int		iCase;
{
register int		iLength;

switch (iCase) {

    case ABOUT_TO_BEGIN:
	miReturn = 0;		/* used as a key counter */
	*mpszUserReturned = Null;
	if (muiUserSize < 2)
	    return (STOP);
	memset(mpszUserReturned, Null, muiUserSize--);
	break;

    case ARE_IN_SECTION:
	if (mpszFileKey) {
	    miReturn++; 	/* used as a key counter */
	    iLength = strlen(strcat(mpszFileKey, "\r\n"));      /* BEWARE! */
	    if (iLength > muiUserSize)
		mpszFileKey[iLength = muiUserSize] = Null;
	    strcpy(mpszUserReturned, mpszFileKey);
	    mpszUserReturned += iLength;  /* effectively concats */
	    if ((muiUserSize -= iLength) == 0)
		return (STOP);	/* no more room */
	    }
	break;

    case NOT_IN_SECTION:
	if (miReturn)		/* used as a key counter */
	    return (STOP);	/* was in, but am now not */
	break;
    }
return (CONTINUE);
}

/*p*/

static	void		CloseFile()
{
if (mpfileConfig) {
    fclose(mpfileConfig);
    mpfileConfig = NULL;
    }
}

/***/

static	void		ParseFile(pszFilespec, pszSection, pfnCallBack)

	char		*pszFilespec,
			*pszSection;
	int		(*pfnCallBack)(int);
{
static	char		szPreviousFile[FILESPEC];	/* previous request */
static	char		szBlankKey[] = " \r\n";         /* used for blank key */

register int		bFileUserSection;
register char		*pC;

ParseKey(mszUserFile, pszFilespec);				/* Filespec */
ParseSection(mszUserSection, pszSection);			/* Section  */
if ((*pfnCallBack)(ABOUT_TO_BEGIN) == CONTINUE) {
    if (mpfileConfig
    and IsStrEquiv(szPreviousFile, mszUserFile))
	rewind(mpfileConfig);
    else {
	CloseFile();
	if (no mlpmemConfig)
	    mpfileConfig = fopen(strcpy(szPreviousFile, mszUserFile), "rt");
	}
    if (mpfileConfig
    or	mlpmemConfig) {
	bFileUserSection = IsStrNull(mszUserSection);	/* in Section? */
	while (mlpmemConfig?
		MemoryGetString(mszFilePre, LINE_SIZE, &mlpmemConfig):
		fgets(mszFilePre, LINE_SIZE, mpfileConfig)) {	/* has '\n' */
	    StrTrunc(mszFilePre, " \t\r\n");
	//  StrDetab(mszFilePre);
	    mpszFileKey = mpszFileString = NULL;  /* assume no "Key = String" */
	    *mszFilePost = Null;
	    if (!IsStrNull(mszFilePre)
	    and *mszFilePre != ';') {
		strcpy(mszFilePost, mszFilePre);
		if (pC = strstr(mszFilePost, " ;"))
		    *pC = Null; 	/* remove comments */
		StrCrop(mszFilePost, " ");      /* now "Key = String" */
		if (!IsStrNull(mszFilePost))
		    if (*mszFilePost == '[')    /* assume a "[SectionName]" */
			bFileUserSection = IsStrNull(mszUserSection)?  YES:
				IsStrEquiv(mszUserSection, mszFilePost);
		    else {			/* assume a "Key = string" */
			if (pC = strchr(mszFilePost, '=')) {
			    *pC++ = Null;
			    mpszFileString = StrSkip(pC, " ");
			    StrTrunc(mszFilePost, " ");
			    }
			else
			    mpszFileString = "";  /* BEWARE OF STRCATTING! */
			mpszFileKey = !IsStrNull(mszFilePost)?	mszFilePost:
				szBlankKey;	  /* BEWARE OF STRCATTING! */
			szBlankKey[1] = Null;	/* to allow for strcatting */
			}
		}
	    if ((*pfnCallBack)(bFileUserSection) == STOP)  /* do call-back */
		break;
	    }
	}
    (*pfnCallBack)(END_OF_PROCESS);	/* file may not have been read */
    if (mbWriteRequest or gbConfigCloseFileOnExit)
	CloseFile();
    }
}

/*p*/

static	void		ParseSection(pszTo, pszFrom)

	char		*pszTo,
			*pszFrom;
{
*pszTo++ = '[';                 /* add '[' */
if (pszFrom) {
    strncpy(pszTo, StrSkip(pszFrom, " "), NAME_SIZE - 3);
    StrTrunc(pszTo, " ");
    }
else
    *pszTo = Null;
if (IsStrNull(pszTo))		/* if pszTo == "[]", */
    *--pszTo = Null;		/*	SectionNames are to be ignored */
else
    strcat(pszTo, "]");         /* add ']' */
}

/***/

static	void		ParseKey(pszTo, pszFrom)

	char		*pszTo,
			*pszFrom;
{
if (pszFrom) {
    strncpy(pszTo, StrSkip(pszFrom, " "), NAME_SIZE - 1);
    StrTrunc(pszTo, " ");
    }
else
    *pszTo = Null;
if (IsStrNull(pszTo)) { 	/* if pszTo == "", */
    *pszTo++ = ' ';             /*      KeyNames must be at least " " */
    *pszTo = Null;
    }
}

/***/

static	char		*MemoryGetString(pszBuffer, iN, plpmemMemory)

	char		*pszBuffer;
register int		iN;
	char		far **plpmemMemory;
{
register char		cChar;

auto	char		*pszString;

if (	**plpmemMemory == A_EOF or
	**plpmemMemory == Null	/* or
	no iN */  )
    return (NULL);
pszString = pszBuffer;
pszString[--iN] = Null;
while (iN--) {
    while ((cChar = **plpmemMemory) == '\r')
	++*plpmemMemory;
    if ((*pszString = cChar) == '\n')
	*++pszString = Null;
    if (cChar)
	++*plpmemMemory;
    if (no *pszString++)
	break;
    }
return (pszBuffer);
}
