/*-------
 * GetPrivateProfileString()
 *
 * approximate implementation of
 * Windows NT System Services version of GetPrivateProfileString()
 * probably doesn't handle the NULL key for section name or value key
 * correctly also, doesn't provide Microsoft backwards compatability
 * wrt TAB characters in the value string
 *
 * Microsoft terminates value
 * at the first TAB, but I couldn't discover what the behavior should
 * be regarding TABS in quoted strings so, I treat tabs like any other
 * characters
 *
 * NO comments following value string separated by a TAB
 * are allowed (that is an anachronism anyway)
 * Added code to search for ODBC_INI file in users home directory on
 * Unix
 *-------
 */

#if !defined(WIN32) && !defined(WITH_UNIXODBC) && !defined(WITH_IODBC)

#include "gpps.h"

#include <stdio.h>
#include <unistd.h>
#include <ctype.h>

#if HAVE_PWD_H
#include <pwd.h>
#endif

#include <sys/types.h>
#include <string.h>
#include "misc.h"
#include "dlg_specific.h"

#ifndef TRUE
#define TRUE	((BOOL)1)
#endif
#ifndef FALSE
#define FALSE	((BOOL)0)
#endif

#ifndef ODBCINSTDIR
#error "ODBCINSTDIR must be defined to compile this file"
#endif


/*
 * theIniFileName is searched for in:
 *	$HOME/theIniFileName
 *	theIniFileName
 *	ODBCINSTDIR/ODBCINST_INI
 */
DWORD
GetPrivateProfileString(const char *theSection, /* section name */
						const char *theKey,		/* search key name */
						const char *theDefault, /* default value if not
												 * found */
						char *theReturnBuffer,	/* return value stored
												 * here */
						size_t theReturnBufferLength,	/* byte length of return
														 * buffer */
						const char *theIniFileName)		/* pathname of ini file
														 * to search */
{
	char		buf[MAXPGPATH];
	char	   *ptr = 0;
	FILE	   *aFile = 0;
	size_t		aLength;
	char		aLine[2048];
	char	   *aValue;
	char	   *aStart;
	char	   *aString;
	size_t		aLineLength;
	size_t		aReturnLength = 0;
	BOOL		aSectionFound = FALSE;
	BOOL		aKeyFound = FALSE;

	ptr = (char *) getpwuid(getuid());	/* get user info */

	if (ptr == NULL || (((struct passwd *) ptr)->pw_dir) == NULL || *(((struct passwd *) ptr)->pw_dir) == '\0')
		ptr = "/home";
	else
		ptr = ((struct passwd *) ptr)->pw_dir;	/* get user home dir */

	/*
	 * If it can't be opened because the paths are too long, then skip it,
	 * don't just truncate the path string...  The truncated path might
	 * accidently be an existing file.	The default value will be returned
	 * instead.
	 */
	if (MAXPGPATH - 1 >= strlen(ptr) + 1 + strlen(theIniFileName))
	{
		sprintf(buf, "%s/%s", ptr, theIniFileName);
		aFile = (FILE *) fopen(buf, PG_BINARY_R);
	}

	/*
	 * This code makes it so that a file in the users home dir overrides a
	 * the "default" file as passed in
	 */
	if (!aFile)
	{
		aFile = (FILE *) fopen(theIniFileName, PG_BINARY_R);
		if (!aFile)
			aFile = (FILE *) fopen(ODBCINSTDIR "/" ODBCINST_INI, PG_BINARY_R);
	}

	aLength = (theDefault == NULL) ? 0 : strlen(theDefault);

	if (theReturnBufferLength == 0 || theReturnBuffer == NULL)
	{
		if (aFile)
			fclose(aFile);
		return 0;
	}

	if (aFile == NULL)
	{
		/* no ini file specified, return the default */
		++aLength;				/* room for NULL char */
		aLength = theReturnBufferLength < aLength ?
			theReturnBufferLength : aLength;
		strncpy(theReturnBuffer, theDefault, aLength);
		theReturnBuffer[aLength - 1] = '\0';
		return aLength - 1;
	}

	while (fgets(aLine, sizeof(aLine), aFile) != NULL)
	{
		aLineLength = strlen(aLine);
		/* strip final '\n' */
		if (aLineLength > 0 && aLine[aLineLength - 1] == '\n')
			aLine[aLineLength - 1] = '\0';
		switch (*aLine)
		{
			case ' ':			/* blank line */
			case ';':			/* comment line */
				continue;
				break;

			case '[':			/* section marker */
				if ((aString = strchr(aLine, ']')))
				{
					aStart = aLine + 1;
					aString--;
					while (isspace((unsigned char) *aStart))
						aStart++;
					while (isspace((unsigned char) *aString))
						aString--;
					*(aString + 1) = '\0';

					/* accept as matched if NULL key or exact match */
					if (!theSection || !strcmp(aStart, theSection))
						aSectionFound = TRUE;
					else
						aSectionFound = FALSE;
				}
				break;

			default:

				/* try to match value keys if in proper section */
				if (aSectionFound)
				{
					/* try to match requested key */
					if ((aString = aValue = strchr(aLine, '=')))
					{
						*aValue = '\0';
						++aValue;

						/* strip leading blanks in value field */
						while (*aValue == ' ' && aValue < aLine + sizeof(aLine))
							*aValue++ = '\0';
						if (aValue >= aLine + sizeof(aLine))
							aValue = "";
					}
					else
						aValue = "";

					aStart = aLine;
					while (isspace((unsigned char) *aStart))
						aStart++;

					/* strip trailing blanks from key */
					if (aString)
					{
						while (--aString >= aStart && *aString == ' ')
							*aString = '\0';
					}

					/* see if key is matched */
					if (theKey == NULL || !strcmp(theKey, aStart))
					{
						/* matched -- first, terminate value part */
						aKeyFound = TRUE;
						aLength = strlen(aValue);

						/* remove trailing blanks from aValue if any */
						aString = aValue + aLength - 1;

						while (--aString > aValue && *aString == ' ')
						{
							*aString = '\0';
							--aLength;
						}

						/* unquote value if quoted */
						if (aLength >= 2 && aValue[0] == '"' &&
							aValue[aLength - 1] == '"')
						{
							/* string quoted with double quotes */

							aValue[aLength - 1] = '\0';
							++aValue;
							aLength -= 2;
						}
						else
						{
							/* single quotes allowed also... */
							if (aLength >= 2 && aValue[0] == '\'' &&
								aValue[aLength - 1] == '\'')
							{
								aValue[aLength - 1] = '\0';
								++aValue;
								aLength -= 2;
							}
						}

						/* compute maximum length copyable */
						aLineLength = (aLength <
						theReturnBufferLength - aReturnLength) ? aLength :
							theReturnBufferLength - aReturnLength;

						/* do the copy to return buffer */
						if (aLineLength)
						{
							strncpy(&theReturnBuffer[aReturnLength],
									aValue, aLineLength);
							aReturnLength += aLineLength;
							if (aReturnLength < theReturnBufferLength)
							{
								theReturnBuffer[aReturnLength] = '\0';
								++aReturnLength;
							}
						}
						if (aFile)
						{
							fclose(aFile);
							aFile = NULL;
						}
						return aReturnLength > 0 ? aReturnLength - 1 : 0;
					}
				}
				break;
		}
	}

	if (aFile)
		fclose(aFile);

	if (!aKeyFound)
	{
		/* key wasn't found return default */
		++aLength;				/* room for NULL char */
		aLength = theReturnBufferLength < aLength ?
			theReturnBufferLength : aLength;
		strncpy(theReturnBuffer, theDefault, aLength);
		theReturnBuffer[aLength - 1] = '\0';
		aReturnLength = aLength - 1;
	}
	return aReturnLength > 0 ? aReturnLength - 1 : 0;
}


DWORD
WritePrivateProfileString(const char *theSection,		/* section name */
						  const char *theKey,	/* write key name */
						  const char *theBuffer,		/* input buffer */
						  const char *theIniFileName)	/* pathname of ini file
														 * to write */
{
	return 0;
}


#if NOT_USED
/*
 * Ok. What the hell's the default behaviour for a null input buffer, and null
 * section name. For now if either are null I ignore the request, until
 * I find out different.
 */
DWORD
WritePrivateProfileString(char *theSection,		/* section name */
						  char *theKey, /* write key name */
						  char *theBuffer,		/* input buffer */
						  char *theIniFileName) /* pathname of ini file to
												 * write */
{
	char		buf[MAXPGPATH];
	char	   *ptr = 0;
	FILE	   *aFile = 0;
	size_t		aLength;
	char		aLine[2048];
	char	   *aValue;
	char	   *aString;
	size_t		aLineLength;
	size_t		aReturnLength = 0;

	BOOL		aSectionFound = FALSE;
	BOOL		keyFound = FALSE;
	int			j = 0;

	/* If this isn't correct processing we'll change it later  */
	if (theSection == NULL || theKey == NULL || theBuffer == NULL ||
		theIniFileName == NULL)
		return 0;

	aLength = strlen(theBuffer);
	if (aLength == 0)
		return 0;

	j = strlen(theIniFileName) + 1;
	ptr = (char *) getpwuid(getuid());	/* get user info */

	if (ptr == NULL)
	{
		if (MAXPGPATH - 1 < j)
			theIniFileName[MAXPGPATH - 1] = '\0';

		sprintf(buf, "%s", theIniFileName);
	}
	ptr = ((struct passwd *) ptr)->pw_dir;		/* get user home dir */
	if (ptr == NULL || *ptr == '\0')
		ptr = "/home";

	/*
	 * This doesn't make it so we find an ini file but allows normal
	 * processing to continue further on down. The likelihood is that the
	 * file won't be found and thus the default value will be returned.
	 */
	if (MAXPGPATH - 1 < strlen(ptr) + j)
	{
		if (MAXPGPATH - 1 < strlen(ptr))
			ptr[MAXPGPATH - 1] = '\0';
		else
			theIniFileName[MAXPGPATH - 1 - strlen(ptr)] = '\0';
	}

	sprintf(buf, "%s/%s", ptr, theIniFileName);

	/*
	 * This code makes it so that a file in the users home dir overrides a
	 * the "default" file as passed in
	 */
	aFile = (FILE *) (buf ? fopen(buf, "r+") : NULL);
	if (!aFile)
	{
		sprintf(buf, "%s", theIniFileName);
		aFile = (FILE *) (buf ? fopen(buf, "r+") : NULL);
		if (!aFile)
			return 0;
	}

	aLength = strlen(theBuffer);

	/*
	 * We have to search for theKey, because if it already exists we have
	 * to overwrite it. If it doesn't exist we just write a new line to
	 * the file.
	 */
	while (fgets(aLine, sizeof(aLine), aFile) != NULL)
	{
		aLineLength = strlen(aLine);
		/* strip final '\n' */
		if (aLineLength > 0 && aLine[aLineLength - 1] == '\n')
			aLine[aLineLength - 1] = '\0';
		switch (*aLine)
		{
			case ' ':			/* blank line */
			case ';':			/* comment line */
				continue;
				break;

			case '[':			/* section marker */
				if ((aString = strchr(aLine, ']')))
				{
					*aString = '\0';

					/* accept as matched if key exact match */

					if (!strcmp(aLine + 1, theSection))
						aSectionFound = TRUE;
				}
				break;

			default:
				/* try to match value keys if in proper section */
				if (aSectionFound)
				{
					/* try to match requested key */

					if ((aString = aValue = strchr(aLine, '=')))
					{
						*aValue = '\0';
						++aValue;

						/* strip leading blanks in value field */
						while (*aValue == ' ' && aValue < aLine + sizeof(aLine))
							*aValue++ = '\0';
						if (aValue >= aLine + sizeof(aLine))
							aValue = "";
					}
					else
						aValue = "";

					/* strip trailing blanks from key */
					if (aString)
					{
						while (--aString >= aLine && *aString == ' ')
							*aString = '\0';
					}

					/* see if key is matched */
					if (!strcmp(theKey, aLine))
					{
						keyFound = TRUE;
						/* matched -- first, terminate value part */

						/* overwrite current value */
						fseek(aFile, -aLineLength, SEEK_CUR);
						/* overwrite key and value */
						sprintf(aLine, "%s = %s\n", theKey, theBuffer);
						fputs(aLine, aFile);
					}
				}
		}
		break;
	}
}

if (!keyFound)
{								/* theKey wasn't in file so  */
	if (aFile)
		fclose(aFile);

	return aReturnLength > 0 ? aReturnLength - 1 : 0;
}
#endif	 /* NOT_USED */

#endif	 /* not WIN32 */
