/*
 * tools.c: Various tools
 *
 * See the main source file 'vdr.c' for copyright information and
 * how to reach the author.
 *
 * $Id: tools.c 1.103 2005/11/04 16:33:18 kls Exp $
 */

#include "..\stdafx.h"
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "..\asprintf.h"
#include "tools.h"

char *skipspace(const char *s)
{
	while (*s && isspace(*s))
		s++;
	return (char *)s;
}

char *stripspace(char *s)
{
	if (s && *s) {
		for (char *p = s + strlen(s) - 1; p >= s; p--) {
			if (!isspace(*p))
				break;
			*p = 0;
		}
	}
	return s;
}

cString AddDirectory(const char *DirName, const char *FileName)
{
	char *buf;
	asprintf(&buf, "%s\\%s", DirName && *DirName ? DirName : ".", FileName);
	return cString(buf, true);
}

char *ReadLink(const char *FileName)
{
	char RealName[_MAX_PATH];
	const char *TargetName = NULL;
	int n = readlink(FileName, RealName, sizeof(RealName) - 1);
	if (n < 0) {
		if (errno == ENOENT || errno == EINVAL) // file doesn't exist or is not a symlink
			TargetName = FileName;
		else // some other error occurred
			printf("ERROR: $s\n",FileName);
	}
	else if (n < int(sizeof(RealName))) { // got it!
		RealName[n] = 0;
		TargetName = RealName;
	}
	else
		printf("ERROR: symlink's target name too long: %s\n", FileName);
	return TargetName ? _strdup(TargetName) : NULL;
}

// --- cTimeMs ---------------------------------------------------------------

cTimeMs::cTimeMs(void)
{
	Set();
}

uint64 cTimeMs::Now(void)
{
	struct timeval t;
	if (gettimeofday(&t, NULL) == 0)
		return (uint64(t.tv_sec)) * 1000 + t.tv_usec / 1000;
	return 0;
}

void cTimeMs::Set(int Ms)
{
	begin = Now() + Ms;
}

bool cTimeMs::TimedOut(void)
{
	return Now() >= begin;
}

uint64 cTimeMs::Elapsed(void)
{
	return Now() - begin;
}

// --- cString ---------------------------------------------------------------

cString::cString(const char *S, bool TakePointer)
{
	s = TakePointer ? (char *)S : S ? _strdup(S) : NULL;
}

cString::~cString()
{
	free(s);
}

cString cString::sprintf(const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	char *buffer;
	vasprintf(&buffer, fmt, ap);
	return cString(buffer, true);
}

// --- cSafeFile -------------------------------------------------------------

cSafeFile::cSafeFile(const char *FileName)
{
	f = NULL;
	fileName = ReadLink(FileName);
	tempName = fileName ? MALLOC(char, strlen(fileName) + 5) : NULL;
	if (tempName)
		strcat(strcpy(tempName, fileName), ".$$$");
}

cSafeFile::~cSafeFile()
{
	if (f)
		fclose(f);
	_unlink(tempName);
	free(fileName);
	free(tempName);
}

bool cSafeFile::Open(void)
{
	if (!f && fileName && tempName) {
		f = fopen(tempName, "w");
		if (!f)
			printf("ERROR: %s\n",tempName);
	}
	return f != NULL;
}

bool cSafeFile::Close(void)
{
	bool result = true;
	if (f) {
		if (ferror(f) != 0) {
			printf("ERROR: %s\n",tempName);
			result = false;
		}
		if (fclose(f) < 0) {
			printf("ERROR: %s\n",tempName);
			result = false;
		}
		f = NULL;
		if (result && MoveFileEx(tempName, fileName, MOVEFILE_REPLACE_EXISTING) < 0) {
			printf("ERROR: %s\n",fileName);
			result = false;
		}
	}
	else
		result = false;
	return result;
}

// --- cListObject -----------------------------------------------------------

cListObject::cListObject(void)
{
  prev = next = NULL;
}

cListObject::~cListObject()
{
}

void cListObject::Append(cListObject *Object)
{
	next = Object;
	Object->prev = this;
}

void cListObject::Insert(cListObject *Object)
{
	prev = Object;
	Object->next = this;
}

void cListObject::Unlink(void)
{
	if (next)
		next->prev = prev;
	if (prev)
		prev->next = next;
	next = prev = NULL;
}

// --- cListBase -------------------------------------------------------------

cListBase::cListBase(void)
{ 
  objects = lastObject = NULL;
  count = 0;
}

cListBase::~cListBase()
{
  Clear();
}

void cListBase::Add(cListObject *Object, cListObject *After)
{
	if (After && After != lastObject) {
		After->Next()->Insert(Object);
		After->Append(Object);
	}
	else {
		if (lastObject)
			lastObject->Append(Object);
		else
			objects = Object;
		lastObject = Object;
	}
	count++;
}

void cListBase::Del(cListObject *Object, bool DeleteObject)
{
	if (Object == objects)
		objects = Object->Next();
	if (Object == lastObject)
		lastObject = Object->Prev();
	Object->Unlink();
	if (DeleteObject)
		delete Object;
	count--;
}

void cListBase::Clear(void)
{
  while (objects) {
        cListObject *object = objects->Next();
        delete objects;
        objects = object;
        }
  objects = lastObject = NULL;
  count = 0;
}

cListObject *cListBase::Get(int Index) const
{
	if (Index < 0)
		return NULL;
	cListObject *object = objects;
	while (object && Index-- > 0)
		object = object->Next();
	return object;
}