/*
 * This file is part of DesktopBSD, see the README file.
 *
 * Author: Peter Hofer <ph@desktopbsd.net>
 *         (C) 2004-2008
 *
 * Copyright: BSD, see the COPYING file included in this distribution
 *
 */

#ifndef __SYSCTL_H
#define __SYSCTL_H

/* strlen() */
#include <cstring>

/* Qt */
#include <qstring.h>

/* sysctl */
#include <sys/types.h>
#include <sys/sysctl.h>

/** @brief Read and write sysctl values. */

class SysCtl
{

public:
	/** Sets an integer sysctl variable.
	  *
	  * @return TRUE when sysctlbyname() succeeded,
	  *         otherwise FALSE.
	  */
	static bool setIntCtl(const char *name, int value)
	{
		return  sysctlbyname(name, NULL, NULL, &value, sizeof(value)) == 0;
	}

	/** Sets a string sysctl variable.
	  *
	  * @return TRUE when sysctlbyname() succeeded,
	  *         otherwise FALSE.
	  */
	static bool setStrCtl(const char *name, const char *value)
	{
		return sysctlbyname(name, NULL, NULL, (void*) value, strlen(value)) == 0;
	}

	/** @return The value of a sysctl integer variable or
	  *         -1 if sysctlbyname() failed.
	  */
	static int getIntCtl(const char *name)
	{
		int     value = -1;
		size_t  len = sizeof(value);
		sysctlbyname(name, &value, &len, NULL, 0);
		return  value;
	}

	/** @return The value of a sysctl integer variable as
	  *         long integer or -1 if sysctlbyname() failed.
	  */
	static long getLongCtl(const char *name)
	{
		long    value = -1;
		size_t  len = sizeof(value);
		sysctlbyname(name, &value, &len, NULL, 0);
		return  value;
	}

	/** @return The value of a sysctl string variable
	  *         or QString::null on failure.
	  */
	static QString getStrCtl(const char *name)
	{
		char *data = getDataCtl(name, NULL);
		QString result(data);
		delete[] data;
		return result;
	}

	/** @return The value of a sysctl string variable as a QCString.
	  *         On failure, a null QCString is created for which
	  *         isNull() returns TRUE.
	  *         Only use this when the internal representation is
	  *         required, otherwise call getStrCtl() instead.
	  */
	static QCString getCStrCtl(const char *name)
	{
		char *data = getDataCtl(name, NULL);
		QCString result(data);
		delete[] data;
		return result;
	}

	/** Returns TRUE if a sysctl with the given name exists,
	  * otherwise FALSE.
	  */
	static bool ctlExists(const char *name)
	{
		int  mib[8];
		size_t len = sizeof mib / sizeof(mib[0]);
		return (sysctlnametomib(name, mib, &len) == 0);
	}

private:
	/** Reads all data for the sysctl identified by the given name.
	  *
	  * @param name Name of the sysctl to retrieve data for.
	  * @param length Pointer to a size_t value that will contain
	  *               the length of the retrieved data. Specify
	  *               NULL to discard the value.
	  * @return Pointer to the data allocated with new[]. The buffer is
	  *         one byte larger than indicated by the updated value of
	  *         "length", which contains a NUL terminator (for strings).
	  *         It's the caller's responsibility to call delete[] to
	  *         dispose the allocated memory. NULL is returned on failure.
	  */
	static char * getDataCtl(const char *name, size_t *length)
	{
		size_t lenval;
		if (length == NULL)
		{
			length = &lenval;
		}

		if (sysctlbyname(name, NULL, length, NULL, 0))
		{
			return (char *) NULL;
		}

		// Allocate an additional byte for a NUL terminator
		char *buffer = new char[*length + 1];
		if (sysctlbyname(name, buffer, length, NULL, 0))
		{
			delete buffer;
			return (char *) NULL;
		}

		// Add a NUL terminator (not included in 
		buffer[*length] = '\0';
		return buffer;
	}
};

#endif /* __SYSCTL_H */
