
//	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-
//
//	Gerbil
//
//	Copyright (c) 2001, Bruce Moreland.  All rights reserved.
//
//	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-
//
//	This file is part of the Gerbil chess program project.
//
//	Gerbil is free software; you can redistribute it and/or modify it under
//	the terms of the GNU General Public License as published by the Free
//	Software Foundation; either version 2 of the License, or (at your option)
//	any later version.
//
//	Gerbil is distributed in the hope that it will be useful, but WITHOUT ANY
//	WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
//	FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
//	details.
//
//	You should have received a copy of the GNU General Public License along
//	with Gerbil; if not, write to the Free Software Foundation, Inc.,
//	59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-

//	--- LINUX PORT NOTE ---
//	This is the file Bruce Moreland's own comment below points to: "if
//	you are porting this away from windows...".  Everything in the
//	original file (GetTickCount, GetSystemTime, GetPrivateProfileString/
//	Int, GetModuleFileName, SetThreadPriority) was Win32-only and has
//	been replaced with POSIX/Linux equivalents:
//	  - TmNow           -> clock_gettime(CLOCK_MONOTONIC, ...)
//	  - VReseed         -> seeded from clock_gettime + pid
//	  - VGetIniFile     -> looks for "gerbil.ini" next to /proc/self/exe
//	  - VGetProfileSz / IGetProfileInt -> tiny hand-rolled INI reader,
//	                       since Linux has no GetPrivateProfileString
//	  - VLowPriority    -> setpriority(PRIO_PROCESS, ...)
//	------------------------

#include "engine.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/resource.h>

#ifdef	DEBUG
static char const s_aszModule[] = __FILE__;
#endif

//	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-

//	Returns system time in milliseconds.  Uses a monotonic clock, which is
//	actually a better fit here than GetTickCount ever was.

unsigned TmNow(void)
{
	struct timespec	ts;

	clock_gettime(CLOCK_MONOTONIC, &ts);
	return (unsigned) ((uint64_t) ts.tv_sec * 1000 +
		(uint64_t) ts.tv_nsec / 1000000);
}

//	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-

void VReseed(void)
{
	struct timespec	ts;

	clock_gettime(CLOCK_REALTIME, &ts);
	srand((unsigned) ((ts.tv_sec ^ ts.tv_nsec ^ getpid())) & 0x7FFF);
}

//	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-

//	The original looked next to the running .exe for "gerbil.ini".  We do
//	the same thing here, using /proc/self/exe (Linux-specific, but this is
//	the Linux port).  If that can't be read for some reason, fall back to
//	just "gerbil.ini" in the current directory.

void VGetIniFile(char * szOut, int cbOut)
{
	ssize_t	cb;
	int	i;

	cb = readlink("/proc/self/exe", szOut, cbOut - 1);
	if (cb <= 0) {
		strncpy(szOut, "gerbil.ini", cbOut);
		szOut[cbOut - 1] = '\0';
		return;
	}
	szOut[cb] = '\0';
	for (i = (int) cb - 1; i >= 0; i--)
		if (szOut[i] == '/') {
			strcpy(szOut + i + 1, "gerbil.ini");
			return;
		}
	//	No slash found -- shouldn't happen with an absolute path from
	//	readlink, but be safe about it.
	strncpy(szOut, "gerbil.ini", cbOut);
	szOut[cbOut - 1] = '\0';
}

static char const s_aszApp[] = "settings";

//	Very small INI reader, replacing GetPrivateProfileString/Int.  It
//	understands the same file layout Gerbil actually ships (README.TXT /
//	gerbil.ini): a "[settings]" section containing "Key=Value" lines.  Lines
//	starting with ";" or "#" are treated as comments.

static BOOL FFindProfileLine(const char * szKey, char * szValOut, int cbValOut)
{
	char	aszPath[512];
	FILE *	fp;
	char	aszLine[512];
	BOOL	fInSection = fFALSE;
	BOOL	fFound = fFALSE;

	VGetIniFile(aszPath, sizeof(aszPath));
	fp = fopen(aszPath, "r");
	if (fp == NULL)
		return fFALSE;
	while (fgets(aszLine, sizeof(aszLine), fp) != NULL) {
		char *	sz = aszLine;
		char *	szEnd;
		size_t	cch;

		while ((*sz == ' ') || (*sz == '\t'))
			sz++;
		cch = strlen(sz);
		while ((cch > 0) &&
			((sz[cch - 1] == '\n') || (sz[cch - 1] == '\r') ||
			 (sz[cch - 1] == ' ') || (sz[cch - 1] == '\t'))) {
			sz[--cch] = '\0';
		}
		if ((cch == 0) || (sz[0] == ';') || (sz[0] == '#'))
			continue;
		if (sz[0] == '[') {
			szEnd = strchr(sz, ']');
			if (szEnd != NULL)
				*szEnd = '\0';
			fInSection = (strcasecmp(sz + 1, s_aszApp) == 0);
			continue;
		}
		if (!fInSection)
			continue;
		szEnd = strchr(sz, '=');
		if (szEnd == NULL)
			continue;
		*szEnd = '\0';
		if (strcasecmp(sz, szKey) == 0) {
			strncpy(szValOut, szEnd + 1, cbValOut - 1);
			szValOut[cbValOut - 1] = '\0';
			fFound = fTRUE;
			break;
		}
	}
	fclose(fp);
	return fFound;
}

void VGetProfileSz(const char * szKey, const char * szDefault,
	char * szOut, int cbOut)
{
	if (!FFindProfileLine(szKey, szOut, cbOut)) {
		strncpy(szOut, szDefault, cbOut - 1);
		szOut[cbOut - 1] = '\0';
	}
}

int IGetProfileInt(const char * szKey, int iDefault)
{
	char	aszVal[256];

	if (!FFindProfileLine(szKey, aszVal, sizeof(aszVal)))
		return iDefault;
	return atoi(aszVal);
}

//	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-

void VLowPriority(void)
{
	setpriority(PRIO_PROCESS, 0, 10);
}

//	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-	-
