 /* GKrellM
|  Copyright (C) 1999-2005 Bill Wilson
|
|  Author:  Bill Wilson    bill@gkrellm.net
|  Latest versions might be found at:  http://gkrellm.net
|
|  Windows code: Bill Nalen bill@nalens.com
|
|  This program is free software which I release under the GNU General Public
|  License. You may redistribute and/or modify this program under the terms
|  of that license as published by the Free Software Foundation; either
|  version 2 of the License, or (at your option) any later version.
|
|  This program 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.  Version 2 is in the
|  COPYRIGHT file in the top level directory of this distribution.
| 
|  To get a copy of the GNU General Puplic License, write to the Free Software
|  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#if defined(WIN32_CLIENT)
#include "../gkrellm.h"
#include "../gkrellm-sysdeps.h"
#include "../gkrellm-private.h"
#include "../win32-plugin.h"
#else
#include "../../server/gkrellmd.h"
#define	GKRELLM_DIR				".gkrellm2"
#endif

#include "../inet.h"

#include <limits.h>
#include <errno.h>
#include <largeint.h>
#include <WINIOCTL.H>
#include <TCHAR.H>

#if defined(USE_PDH)
// windows NT/2k/XP
#include <pdh.h>
#include <PDHMSG.H>
#include <LMCONS.H>
#include <lmwksta.h>
#include <lmapibuf.h>
#else
// windows 95/98/Me
#include <tlhelp32.h>
#endif

// *****************************************************************

#define MAX_NET_NAME 6
#define MAX_NET_ADAPTERS 10
#define MAX_DISK_NAME 6
#define MAX_DISKS 10
#define MAX_CPU 6

#if defined(USE_PDH)
    // windows NT/2k/XP
    #define numPerfKeys 12
#else
    // windows 95/98/Me
    #define numPerfKeys 6
#endif

//******************************************************************

static gint		numCPUs;
static gulong	swapin, swapout;
static TCHAR netName[MAX_NET_ADAPTERS + 1][MAX_NET_NAME + 1];
static TCHAR diskName[MAX_DISKS + 1][MAX_DISK_NAME + 1];
static gint numAdapters = 0;
static int rx[MAX_NET_ADAPTERS + 1], tx[MAX_NET_ADAPTERS + 1];
static int initDone = 0;
static OSVERSIONINFO info;
static int fswin95 = 0;
static gchar* sname;
static gchar* hostname;

#if defined(WIN32_CLIENT)
    static win32_plugin_callbacks plugin_callbacks;
    win32_plugin_callbacks* callbacks;
#endif
static TCHAR* perfKeys[numPerfKeys];

#if defined(USE_PDH)  // windows NT/2k/XP
    static HQUERY query = 0;
    static HCOUNTER cpuUserCounter[MAX_CPU + 1];
    static HCOUNTER cpuSysCounter[MAX_CPU + 1];
    static HCOUNTER processCounter;
    static HCOUNTER threadCounter;
    static HCOUNTER uptimeCounter;
    static HCOUNTER diskReadCounter[MAX_DISKS + 1];
    static HCOUNTER diskWriteCounter[MAX_DISKS + 1];
    static HCOUNTER netRecCounter[MAX_NET_ADAPTERS + 1];
    static HCOUNTER netSendCounter[MAX_NET_ADAPTERS + 1];
    static PDH_STATUS status;
    static int winVer = 1;
#else  // windows 95/98/Me
    HKEY cpuperfKey;
    HKEY threadPerfKey;
    HKEY diskreadPerfKey;
    HKEY diskwritePerfKey;
    HKEY netPerfKey[MAX_NET_ADAPTERS + 1];
    DWORD dataType,dataLen=8192;
    char data[8192];	// Buffer to communicate with registry. (way bigger than neeeded)
    static int winVer = 0;  // used for make Pdh_ calls for performance data under NT and higher
#endif

// *****************************************************************

// local function protos
void win32_sys_cpu_stop(void);
void win32_sys_disk_stop(void);
void win32_init_callbacks(void);
void win32_sys_process_stop(void);
void win32_sys_net_stop(void);
void readPerfKeyFile(void);
void placePerfKey(const char* key, const char* item);

#if !defined(WIN32_CLIENT)
gchar *gkrellm_homedir(void)
{
	gchar	*homedir;

	homedir = (gchar *) g_get_home_dir();
	if (!homedir)
		homedir = ".";
	return homedir;
}
#endif

#if defined(WIN32) && defined(_WINDOWS) && !defined(WIN32_CLIENT)
UINT WM_GKRELLMCALLBACK;
int notDone;

LRESULT CALLBACK wndProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
    if (uMsg == WM_GKRELLMCALLBACK && lParam == WM_RBUTTONUP) {
        PostQuitMessage(0);
        notDone = 1;
        return 0;
    }
    else {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

void setupServerWindow(HINSTANCE hInstance)
{
    NOTIFYICONDATA nid;
    HWND hwnd;
    WNDCLASSEX wndClass;
    char* CLASS_NAME = "GKrellMServer";

    notDone = 0;
        
    wndClass.cbSize = sizeof(WNDCLASSEX);
    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = wndProc;
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;
    wndClass.hIcon = NULL;
    wndClass.hCursor = NULL;
    wndClass.hbrBackground = 0;
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = CLASS_NAME;
    wndClass.hIconSm = NULL;
    RegisterClassEx(&wndClass);
    
    hwnd = CreateWindow(CLASS_NAME, "GKrellMServer", SW_HIDE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);

    // Create System Tray Icon
    WM_GKRELLMCALLBACK = RegisterWindowMessage(TEXT("GKrellMCallback"));

    nid.cbSize = sizeof(NOTIFYICONDATA);                
    nid.hWnd = hwnd;
    nid.uID = 1;   
    nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
    nid.uCallbackMessage = WM_GKRELLMCALLBACK;                                                                               
    strcpy(nid.szTip,"GKrellM Server for Windows");
    nid.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON3));
    Shell_NotifyIcon(NIM_ADD, &nid);
}
#endif


//***************************************************************************

void gkrellm_sys_main_init(void)
{
    WSADATA wsdata;
    int err;
    HMODULE mod = 0;
    FARPROC proc = 0;

    // initialize winsock
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Starting Winsock\n");
    err = WSAStartup(MAKEWORD(1,1), &wsdata);
    if (err != 0 && _GK.debug_level & DEBUG_SYSDEP) 
        printf("Starting Winsock failed with error code %i\n", err);

#if defined(USE_PDH)
    if (query == 0 && winVer > 0) {
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Opening Pdh");
        status = PdhOpenQuery(NULL, 0, &query);
        if (status != ERROR_SUCCESS) {
            if (_GK.debug_level & DEBUG_SYSDEP)
                printf("Opening Pdh failed with error code %i\n", status);
        }
    }
#endif

    // do this once
    info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&info);

    // see if we can use the GetDiskFreeSpaceEx function
    mod = GetModuleHandle("KERNEL32.DLL");
    proc = GetProcAddress(mod, "GetDiskFreeSpaceExA");
    if (proc != NULL) {
        fswin95 = 1;
    }

    // we don't have local mail on Windows (yet?)
    gkrellm_mail_local_unsupported();
    
    // try to make nice
    //SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);

    // initialize call back structure for plugins
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing callbacks\n");
    win32_init_callbacks();

    // get registry key names
    readPerfKeyFile();
}

void gkrellm_sys_main_cleanup(void)
{
    int i;
#if defined(WIN32_CLIENT)
    NOTIFYICONDATA nid;

    // remove system tray icon
    nid.cbSize = sizeof(NOTIFYICONDATA);                
    nid.hWnd = GDK_WINDOW_HWND(gkrellm_get_top_window()->window);
    nid.uID = 1;   
    nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
    nid.uCallbackMessage = WM_CLOSE;                                                                               
    strcpy(nid.szTip,"GKrellM for Windows");
    nid.hIcon = LoadIcon(NULL, IDI_WINLOGO);
    Shell_NotifyIcon(NIM_DELETE, &nid);

    if (_GK.withdrawn) {
        // remove from bluebox slit
		HWND hwnd = FindWindowEx(NULL, NULL, "BControl", "BSlitWindow");
    	SendMessage(hwnd, BM_SLITMESSAGE, BSM_DELETEWINDOW, (LPARAM) GDK_WINDOW_HWND(gkrellm_get_top_window()->window));
		RemoveProp(GDK_WINDOW_HWND(gkrellm_get_top_window()->window), "BSlitControl");
		SetParent(GDK_WINDOW_HWND(gkrellm_get_top_window()->window), NULL);
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Removing from slit\n");
		//SetWindowPos(0, m_nLeft, m_nTop, m_nWidth, m_nHeight, SWP_NOZORDER);
    }

#endif

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Waiting for mail checking thread to end.\n");
    while (gkrellm_mail_get_active_thread() != NULL) {
        // wait here till it finishes
        // in case we are trying to get mail info
        Sleep(500);
    }

    // stop performance gathering
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Stopping perfomance monitoring.\n");
    win32_sys_cpu_stop();
    win32_sys_disk_stop();
    win32_sys_process_stop();
    win32_sys_net_stop();

    // close keys
#if defined(USE_PDH)
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Closing Pdh\n");
    PdhCloseQuery(query);
#else
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Closing registry keys.\n");
    if (cpuperfKey != 0)
        RegCloseKey(cpuperfKey);	
    if (threadPerfKey != 0)
        RegCloseKey(threadPerfKey);
    if (diskreadPerfKey != 0)
        RegCloseKey(diskreadPerfKey);	
    if (diskwritePerfKey != 0)
        RegCloseKey(diskwritePerfKey);	
    for (i = 0; i < numAdapters; i++)
        RegCloseKey(netPerfKey[i]);
#endif

    // free up these strings
    for (i = 0; i < numPerfKeys; i++)
        free(perfKeys[i]);

    g_free(sname);
    g_free(hostname);

    // stop winsock
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Closing Winsock\n");
    WSACleanup();
}

// only need to collect query data once for all those monitors that use it
static void win32_read_proc_stat(void)
	{
	static gint	data_read_tick	= -1;

	if (data_read_tick == gkrellm_get_timer_ticks())	/* One read per tick */
		return;

	data_read_tick = gkrellm_get_timer_ticks();

#if defined(USE_PDH)
    if (winVer > 0 && query != 0) {
        status = PdhCollectQueryData(query);
    }
#endif

	}


/* ===================================================================== */
/* Sensor interface */
/* ===================================================================== */

// interface to work with shared memory for MBM5

// ---------------------------------------------------------------------------
// --------------------------------------- Copyright 2001 A@majland.org ------
// --------------------------------------- Alteration for use in Visual C ----
// --------------------------------------- By Chris Zahrt techn0@iastate.edu -
// ---------------------------------------------------------------------------
//
//  Version     : 0.1
//  Date        : 02-27-2002
//
//  MBM         : version 5.1
//
//  Author      : Chris Zahrt techn0@iastate.edu (visual c alterations)
//                http://techn0.dhs.org/programming/vcmbmsm.html
//                Anders@Majland.org (author of original c code)
//                http://www.majland.org/sw/mbmcaf
//
//  Licence     : Cardware. (Send me a note/email if you find it usefull.)
//                Basically you may use it as you see fit as long as the origin
//                of the code remains clear
//
//  History     :
//		  0.1 02-27-2002 conversion of 0.3 borland to this version

// Update for MBM 5.1.9 by Bill Nalen bill@nalens.com

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

#define NrTemperature 32
#define NrVoltage 16
#define NrFan 16
#define NrCPU 4

static double temperatures[NrTemperature];
static double voltages[NrVoltage];
static double fans[NrFan];

//    enum Bus
#define BusType     char
#define ISA         0
#define SMBus       1
#define VIA686Bus   2
#define DirectIO    3

//    enum SMB
#define SMBType         char
#define smtSMBIntel     0
#define smtSMBAMD       1
#define smtSMBALi       2
#define smtSMBNForce    3
#define smtSMBSIS       4

// enum Sensor Types
#define SensorType      char
#define stUnknown       0
#define stTemperature   1
#define stVoltage       2
#define stFan           3
#define stMhz           4
#define stPercentage    5    

typedef struct {
    SensorType  iType;          // type of sensor
    int         Count;          // number of sensor for that type
} SharedIndex;

typedef struct {
    SensorType ssType;          // type of sensor
    unsigned char ssName[12];   // name of sensor
    char sspadding1[3];         // padding of 3 byte
    double ssCurrent;           // current value
    double ssLow;               // lowest readout
    double ssHigh;              // highest readout
    long ssCount;               // total number of readout
    char sspadding2[4];         // padding of 4 byte
    long double ssTotal;        // total amout of all readouts
    char sspadding3[6];         // padding of 6 byte
    double ssAlarm1;            // temp & fan: high alarm; voltage: % off;
    double ssAlarm2;            // temp: low alarm
} SharedSensor;

typedef struct {
    short siSMB_Base;            // SMBus base address
    BusType siSMB_Type;         // SMBus/Isa bus used to access chip
    SMBType siSMB_Code;         // SMBus sub type, Intel, AMD or ALi
    char siSMB_Addr;            // Address of sensor chip on SMBus
    unsigned char siSMB_Name[41];        // Nice name for SMBus
    short siISA_Base;            // ISA base address of sensor chip on ISA
    int siChipType;             // Chip nr, connects with Chipinfo.ini
    char siVoltageSubType;      // Subvoltage option selected
} SharedInfo;

typedef struct {
    double sdVersion;           // version number (example: 51090)
    SharedIndex sdIndex[10];     // Sensor index
    SharedSensor sdSensor[100];  // sensor info
    SharedInfo sdInfo;          // misc. info
    unsigned char sdStart[41];           // start time
    unsigned char sdCurrent[41];         // current time
    unsigned char sdPath[256];           // MBM path
} SharedData;

static int tempCount, voltCount, fanCount;

static int ReadSharedData(void)
{
  SharedData *ptr;
  SharedSensor *sens;
  HANDLE hSData;
  int i, j;
  int totalCount;

  hSData=OpenFileMapping(FILE_MAP_READ, FALSE, "$M$B$M$5$S$D$");
  if (hSData==0) 
      return 1;

  ptr = (SharedData *)MapViewOfFile(hSData, FILE_MAP_READ, 0, 0, 0);
  if (ptr == 0){
      CloseHandle(hSData);
      return 1;
  }

  totalCount = 0;
  for (i = 0; i < 5; i++) {
      totalCount += ptr->sdIndex[i].Count;
  }

  tempCount = 0;
  voltCount = 0;
  fanCount = 0;
  for (j = 0; j < totalCount; j++) {
      sens = &(ptr->sdSensor[j]);

      switch (ptr->sdSensor[j].ssType) {
      case stUnknown:
          break;
      case stTemperature:
          temperatures[tempCount] = ptr->sdSensor[j].ssCurrent;
          ++tempCount;
          break;
      case stVoltage:
          voltages[voltCount] = ptr->sdSensor[j].ssCurrent;
          ++voltCount;
          break;
      case stFan:
          fans[fanCount] = ptr->sdSensor[j].ssCurrent;
          ++fanCount;
          break;
      case stMhz:
          break;
      case stPercentage:
          break;
      }
  }
        
  UnmapViewOfFile(ptr);
  CloseHandle(hSData);

  return 0;
}

gboolean gkrellm_sys_sensors_get_voltage(gchar *device_name, gint id,
		gint iodev, gint inter, gfloat *volt)
{

    if (iodev < NrVoltage && iodev >= 0) {
        if (ReadSharedData() == 1) {
            *volt = 0;
            return FALSE;
        }

        *volt = voltages[iodev];

        return TRUE;
    }
    else {
        *volt = 0;
        return FALSE;
    }
}

gboolean gkrellm_sys_sensors_get_fan(gchar *device_name, gint id,
		gint iodev, gint inter, gfloat *fan)
{

    if (iodev < NrFan && iodev >= 0) {
        if (ReadSharedData() == 1) {
            *fan = 0;
            return FALSE;
        }

        *fan = fans[iodev];    

        return TRUE;
    }
    else {
        *fan = 0;
        return FALSE;
    }
}

gboolean gkrellm_sys_sensors_get_temperature(gchar *device_name, gint id,
		gint iodev, gint inter, gfloat *temp)
{

    if (iodev < NrTemperature && iodev >= 0) {
        if (ReadSharedData() == 1) {
            *temp = 0;
            return FALSE;
        }

        *temp = temperatures[iodev];

        return TRUE;
    }
    else {
        *temp = 0;
        return FALSE;
    }
}

gboolean gkrellm_sys_sensors_init(void)
{
    int haveMBM = 0;
    char buf[25];
//    SharedData *ptr;
//    SharedSensor *sens;
//    HANDLE hSData;
    int i;
//    int j;
//    int totalCount;

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing sensors.\n");

//    hSData=OpenFileMapping(FILE_MAP_READ, FALSE, "$M$B$M$5$S$D$");
//    if (hSData != 0) { 
//        ptr = (SharedData *)MapViewOfFile(hSData, FILE_MAP_READ, 0, 0, 0);
//        if (ptr == 0){
//          CloseHandle(hSData);
//          haveMBM = 0;
//        }
//        else {
//            haveMBM = 1;
//        }
//    }
//    else {
//        haveMBM = 0;
//    }

/*    if (haveMBM == 1) {
        totalCount = 0;
        for (i = 0; i < 5; i++) {
          totalCount += ptr->sdIndex[i].Count;
        }

        tempCount = 0;
        voltCount = 0;
        fanCount = 0;
        for (j = 0; j < totalCount; j++) {
          sens = &(ptr->sdSensor[j]);

          switch (ptr->sdSensor[j].ssType) {
          case stUnknown:
              break;
          case stTemperature:
                gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE, NULL, sens->ssName,
			        tempCount, tempCount, 0,
			        1, 0, NULL, sens->ssName);
                ++tempCount;
              break;
          case stVoltage:
                if (strlen(sens->ssName) == 0) 
                    gkrellm_sensors_add_sensor(SENSOR_VOLTAGE, NULL, "Volt",
		                voltCount, voltCount, 0,
		                1, 0, NULL, "Volt");
                else 
                    gkrellm_sensors_add_sensor(SENSOR_VOLTAGE, NULL, sens->ssName,
		                voltCount, voltCount, 0,
		                1, 0, NULL, sens->ssName);
                ++voltCount;
              break;
          case stFan:
                gkrellm_sensors_add_sensor(SENSOR_FAN, NULL, sens->ssName,
	                fanCount, fanCount, 0,
	                1, 0, NULL, sens->ssName);
                ++fanCount;
              break;
          case stMhz:
              break;
          case stPercentage:
              break;
          }
        }
    
        UnmapViewOfFile(ptr);
        CloseHandle(hSData);
    }
    else  */
    {   
        tempCount = 0;
        voltCount = 0;
        fanCount = 0;

        for (i = 0; i < NrTemperature; i++) {
            sprintf(buf, "Temp %i", i);
            gkrellm_sensors_add_sensor(SENSOR_TEMPERATURE, NULL, buf,
	            tempCount, tempCount, 0,
	            1, 0, NULL, buf);
            ++tempCount;
        }

        for (i = 0; i < NrVoltage; i++) {
            sprintf(buf, "Volt %i", i);
            gkrellm_sensors_add_sensor(SENSOR_VOLTAGE, NULL, buf,
		        voltCount, voltCount, 0,
		        1, 0, NULL, buf);
            ++voltCount;
        }
    
        for (i = 0; i < NrFan; i++) {
            sprintf(buf, "Fan %i", i);
            gkrellm_sensors_add_sensor(SENSOR_FAN, NULL, buf,
	            fanCount, fanCount, 0,
	            1, 0, NULL, buf);
            ++fanCount;
        }
    }

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initialized sensors for %i temps, %i volts, %i fans.\n", tempCount, voltCount, fanCount);

    return TRUE;
}

/* ===================================================================== */
/* CPU monitor interface */
/* ===================================================================== */

void
gkrellm_sys_cpu_read_data(void)
	{
	/* One routine reads cpu, disk, and swap data.  All three monitors will
	| call it, but only the first call per timer tick will do the work.
	*/

#if defined(USE_PDH)
    static gulong user[MAX_CPU], nice[MAX_CPU], sys[MAX_CPU], idle[MAX_CPU];
    DWORD type;
    PDH_FMT_COUNTERVALUE value;
    int i;
#else
    static gulong user, nice, sys, idle;
    DWORD* value;
#endif
    long userInt = 0, idleInt = 0, sysInt = 0;

    win32_read_proc_stat();

#if defined(USE_PDH)
    if (winVer > 0 && query != 0) {
        for (i = 0; i < numCPUs; i++) {
            status = PdhGetFormattedCounterValue(cpuUserCounter[i], PDH_FMT_LONG, &type, &value);
            if (status != 0) 
                userInt = 0;
            else 
                userInt = value.longValue;
    
            status = PdhGetFormattedCounterValue(cpuSysCounter[i], PDH_FMT_LONG, &type, &value);
            if (status != 0)
                sysInt = 0;
            else
                sysInt = value.longValue;

            // user time defined as total - system
            userInt -= sysInt;

            idleInt = 100 - userInt - sysInt;

            user[i] += userInt;
            sys[i] += sysInt;
            idle[i] += idleInt;
   
	        gkrellm_cpu_assign_data(i, user[i], nice[i], sys[i], idle[i]);
        }
    }
#else
    if (cpuperfKey != 0) {
        dataLen = 8192;
        RegQueryValueEx(cpuperfKey, perfKeys[0], NULL, &dataType, data, &dataLen);
        if (dataType == REG_BINARY) {
            sysInt = 0;
            value = data;
            userInt = *value;
        }
    }

    // user time defined as total - system
    userInt -= sysInt;

    idleInt = 100 - userInt - sysInt;

    user += userInt;
    sys += sysInt;
    idle += idleInt;
   
	gkrellm_cpu_assign_data(0, user, nice, sys, idle);
#endif
	}

gboolean
gkrellm_sys_cpu_init(void)
	{
#if !defined(USE_PDH)
    HKEY startPerf;
#else
    SYSTEM_INFO sysInfo;
    int i;
    TCHAR buf[50];
    TCHAR buf2[10];
#endif

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing CPU monitor.\n");

    gkrellm_cpu_nice_time_unsupported();

#if defined(USE_PDH)
    GetSystemInfo(&sysInfo);

    numCPUs = sysInfo.dwNumberOfProcessors;

    if (numCPUs < 1) {
        numCPUs = 1;
    }
    if (numCPUs > MAX_CPU) {
        numCPUs = MAX_CPU;
    }

    if (winVer > 0 && query != 0) {
        for (i = 0; i < numCPUs; i++) {
            _tcscpy(buf, perfKeys[11]);
            _itot(i, buf2, 10);
            _tcscat(buf, buf2);
            _tcscat(buf, perfKeys[0]);
            status = PdhAddCounter(query, buf, 0, &cpuUserCounter[i]);
            if (_GK.debug_level & DEBUG_SYSDEP)
                printf("Initialized cpu %i user portion (%s) with error code %i\n", i, buf, status);

            _tcscpy(buf, perfKeys[11]);
            _itot(i, buf2, 10);
            _tcscat(buf, buf2);
            _tcscat(buf, perfKeys[1]);
            status = PdhAddCounter(query, buf, 0, &cpuSysCounter[i]);
            if (_GK.debug_level & DEBUG_SYSDEP)
                printf("Initialized cpu %i system portion (%s) with error code %i\n", i, buf, status);
        }
    }
#else
    numCPUs = 1;
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing cpu (%s)\n", perfKeys[0]);
    if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StartStat", &startPerf) == ERROR_SUCCESS) {
        // success
        dataLen = 8192;
        if (RegQueryValueEx(startPerf, perfKeys[0], NULL, &dataType, data, &dataLen) != ERROR_SUCCESS) {
            // failed
            if (_GK.debug_level & DEBUG_SYSDEP)
                printf("Initializing cpu failed.\n");
            cpuperfKey = 0;
        }
        else {
            if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StatData", &cpuperfKey) != ERROR_SUCCESS) {
                // failed
                if (_GK.debug_level & DEBUG_SYSDEP)
                    printf("Initializing cpu failed.\n");
                cpuperfKey = 0;
            }
        }
        RegCloseKey(startPerf);
    }
    else {
        // failed
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Initializing cpu failed.\n");
        cpuperfKey = 0;
    }
#endif

	gkrellm_cpu_set_number_of_cpus(numCPUs);

	return TRUE;
	}

void win32_sys_cpu_stop(void)
{
#if !defined(USE_PDH)
    HKEY startPerf;

    if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StopStat", &startPerf) == ERROR_SUCCESS) {
        // success
        dataLen = 8192;
        RegQueryValueEx(startPerf, perfKeys[0], NULL, &dataType, data, &dataLen);
        cpuperfKey = 0;
        RegCloseKey(startPerf);
    }
#endif
}

/* ===================================================================== */
/* Net monitor interface */
/* ===================================================================== */


void gkrellm_sys_net_read_data(void)
{
	gint	i;
#if defined(USE_PDH)
    DWORD type;
    PDH_FMT_COUNTERVALUE value;
#else
    DWORD* value;
#endif

    win32_read_proc_stat();

#if defined(USE_PDH)
    if (winVer > 0 && query != 0) {
        for (i = 0; i < numAdapters; i++) {
            status = PdhGetFormattedCounterValue(netRecCounter[i], PDH_FMT_LONG, &type, &value);
            rx[i] += value.longValue / _GK.update_HZ;

            status = PdhGetFormattedCounterValue(netSendCounter[i], PDH_FMT_LONG, &type, &value);
            tx[i] += value.longValue / _GK.update_HZ;

            gkrellm_net_assign_data(netName[i], rx[i], tx[i]);
        }
    }
#else
    for (i = 0; i < numAdapters; i++) {
        rx[i] = 0;
        tx[i] = 0;
        if (netPerfKey[i] != 0) {
            dataLen = 8192;
            RegQueryValueEx(netPerfKey[i], perfKeys[4], NULL, &dataType, data, &dataLen);
            if (dataType == REG_BINARY) {
                value = data;
                rx[i] = *value;
            }
        }
        gkrellm_net_assign_data(netName[i], rx[i], tx[i]);
    }
#endif
}

void gkrellm_sys_net_check_routes(void)
{
}


gboolean gkrellm_sys_net_isdn_online(void)
{
	return FALSE;	/* ISDN is off line */
}

gboolean gkrellm_sys_net_init(void)
{
#if !defined(USE_PDH)
    HKEY startPerf;
#endif
    DWORD size = 0;
    DWORD isize = 0;
    LPTSTR objects = NULL;
    LPTSTR instances = NULL;
    LPTSTR instance = NULL;
    TCHAR buf[1024];
    int strSize;
    int i;
    int adapter = -1;

    // turn off for now
//	gkrellm_net_add_timer_type_ppp("ppp0");
//	gkrellm_net_add_timer_type_ippp("ippp0");

    numAdapters = 0;
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing network monitor.\n");

#if defined(USE_PDH)
    if (winVer > 0 && query != 0) {
        PdhEnumObjectItems(NULL, NULL, TEXT("Network Interface"), NULL, &size, NULL, &isize, PERF_DETAIL_WIZARD, 0);
        if (_GK.debug_level & DEBUG_SYSDEP) {
            printf("Found %i network objects and %i network instances.\n", size, isize);
        }
        if (size > 0) {
            ++isize;
            ++size;

            objects = (LPTSTR) malloc(sizeof(TCHAR) * size);
            instances = (LPTSTR) malloc(sizeof(TCHAR) * isize);

            PdhEnumObjectItems(NULL, NULL, TEXT("Network Interface"), objects, &size, instances, &isize, PERF_DETAIL_WIZARD, 0);
            if (_GK.debug_level & DEBUG_SYSDEP) {
                printf("Enumerated %i network objects and %i network instances.\n", size, isize);
            }

            for (instance = instances; *instance != 0; instance += lstrlen(instance) + 1) {
                ++adapter;
                if (adapter >= MAX_NET_ADAPTERS) {
                    if (_GK.debug_level & DEBUG_SYSDEP)
                        printf("Hit maximum number of network adapters.\n");
                    break;
                }

                strSize = MAX_NET_NAME;
                if (_tcslen(instance) < MAX_NET_NAME) {
                    strSize = _tcslen(instance);
                }

                for (i = 0; i < strSize; i++) {
                    if (instance[i] == _T(' ')) 
                        netName[adapter][i] = _T('_');
                    else
                        netName[adapter][i] = instance[i];
                }

                netName[adapter][strSize] = _T('\0');

                gkrellm_net_add_timer_type_ppp(netName[adapter]);

                _tcscpy(buf, perfKeys[5]);
                _tcscat(buf, instance);
                _tcscat(buf, perfKeys[6]);
                rx[adapter] = 0;
                status = PdhAddCounter(query, buf, 0, &(netRecCounter[adapter]));
                if (_GK.debug_level & DEBUG_SYSDEP)
                    printf("Added network receive for %s interface with status %i.\n", buf, status);

                _tcscpy(buf, perfKeys[5]);
                _tcscat(buf, instance);
                _tcscat(buf, perfKeys[7]);
                status = PdhAddCounter(query, buf, 0, &(netSendCounter[adapter]));
                tx[adapter] = 0;
                if (_GK.debug_level & DEBUG_SYSDEP)
                    printf("Added network transmit for %s interface with status %i.\n", buf, status);
            } 

            numAdapters = adapter + 1;
        }

        if (objects != NULL)
            free(objects);
        if (instances != NULL)
            free(instances);
    }
#else

    // need to enumerate these
    // and pick up names
    numAdapters = 0;
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing interface for %s with name of %s\n", perfKeys[4], perfKeys[5]);
    if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StartStat", &startPerf) == ERROR_SUCCESS) {
        // success
        dataLen = 8192;
        if (RegQueryValueEx(startPerf, perfKeys[4], NULL, &dataType, data, &dataLen) != ERROR_SUCCESS) { 
            // failed
            netPerfKey[numAdapters] = 0;
            if (_GK.debug_level & DEBUG_SYSDEP)
                printf("Initizialization failed.\n");
        }
        else {
            if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StatData", &(netPerfKey[numAdapters])) != ERROR_SUCCESS) {
                // failed
                netPerfKey[numAdapters] = 0;
                if (_GK.debug_level & DEBUG_SYSDEP)
                    printf("Initizialization failed.\n");
            }
            else {
                // success
                _tcscpy(netName[numAdapters], perfKeys[5]);
                gkrellm_net_add_timer_type_ppp(netName[numAdapters]);
                numAdapters = 1;
            }
        }
        RegCloseKey(startPerf);
    }
    else {
        // failed
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Initizialization failed.\n");
        netPerfKey[0] = 0;
    }
#endif

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Found %i interfaces for monitoring.\n", numAdapters);

    if (numAdapters == 0)
        return FALSE;
    else
        return TRUE;
	}

void win32_sys_net_stop(void)
{
#if !defined(USE_PDH)
    HKEY startPerf;

    if (numAdapters > 0) {
        if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StopStat", &startPerf) == ERROR_SUCCESS) {
            // success
            dataLen = 8192;
            RegQueryValueEx(startPerf, perfKeys[4], NULL, &dataType, data, &dataLen);
            netPerfKey[0] = 0;
            RegCloseKey(startPerf);
        }
    }
#endif
}

/* ===================================================================== */
/* Disk monitor interface */
/* ===================================================================== */

static gint numDisks = 0;
static long diskread[MAX_DISKS], diskwrite[MAX_DISKS];

gchar * gkrellm_sys_disk_name_from_device(gint device_number, gint unit_number,
			gint *order)
{
	static gchar name[32];

	sprintf(name, "Disk%s", diskName[device_number]);
	*order = device_number;
	return name;
}

gint gkrellm_sys_disk_order_from_name(gchar *name)
{
	return 0;	/* Disk by name not implemented in Windows */
}

void gkrellm_sys_disk_read_data(void)
{
	/* One routine reads cpu, disk, and swap data.  All three monitors will
	| call it, but only the first call per timer tick will do the work.
	*/
#if defined(USE_PDH)
    DWORD type;
    PDH_FMT_COUNTERVALUE value;
#else
    DWORD* value;
#endif
    double readInt = 0, writeInt = 0;
    int i;

    win32_read_proc_stat();

#if defined(USE_PDH)
    if (winVer > 0 && query != 0) {
        for (i = 0; i < numDisks; i++) {
            status = PdhGetFormattedCounterValue(diskReadCounter[i], PDH_FMT_DOUBLE, &type, &value);
            readInt = value.doubleValue / _GK.update_HZ;

            status = PdhGetFormattedCounterValue(diskWriteCounter[i], PDH_FMT_DOUBLE, &type, &value);
            writeInt = value.doubleValue / _GK.update_HZ;

            diskread[i] += readInt;
            diskwrite[i] += writeInt;

        }
    }
#else
    if (diskreadPerfKey != 0) {
        dataLen = 8192;
        RegQueryValueEx(diskreadPerfKey, perfKeys[2], NULL, &dataType, data, &dataLen);
        if (dataType == REG_BINARY) {
            value = data;
            readInt = *value;
        }
    }

    if (diskwritePerfKey != 0) {
        dataLen = 8192;
        RegQueryValueEx(diskwritePerfKey, perfKeys[3], NULL, &dataType, data, &dataLen);
        if (dataType == REG_BINARY) {
            value = data;
            writeInt = *value;
        }
    }

    // readInt & writeInt appear to be cumulative already
    diskread[0] = readInt;
    diskwrite[0] = writeInt;

#endif

	for (i = 0; i < numDisks; i++)
		{
//    		gkrellm_disk_assign_data_nth(i, diskread[i], diskwrite[i]);
    	    gkrellm_disk_assign_data_by_device(i, 0,
						diskread[i], diskwrite[i], FALSE);
		}
	}

gboolean gkrellm_sys_disk_init(void)
	{
#if defined(USE_PDH)
    DWORD size = 0;
    DWORD isize = 0;
    LPTSTR objects = NULL;
    LPTSTR instances = NULL;
    LPTSTR instance = NULL;
    TCHAR buf[1024];
    int strSize;
    int i;
    int disk = -1;

    numDisks = 0;

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing disk monitor.\n");

    if (winVer > 0 && query != 0) {
        PdhEnumObjectItems(NULL, NULL, TEXT("PhysicalDisk"), NULL, &size, NULL, &isize, PERF_DETAIL_WIZARD, 0);
        if (size > 0) {
            ++isize;
            ++size;

            objects = (LPTSTR) malloc(sizeof(TCHAR) * size);
            instances = (LPTSTR) malloc(sizeof(TCHAR) * isize);

            PdhEnumObjectItems(NULL, NULL, TEXT("PhysicalDisk"), objects, &size, instances, &isize, PERF_DETAIL_WIZARD, 0);

            for (instance = instances; *instance != 0; instance += lstrlen(instance) + 1) {
                ++disk;
                if (disk >= MAX_DISKS)
                    break;

                strSize = MAX_DISK_NAME;
                if (_tcsclen(instance) < MAX_DISK_NAME) {
                    strSize = _tcsclen(instance);
                }

                for (i = 0; i < strSize; i++) {
                    if (instance[i] == _T(' ')) 
                        diskName[disk][i] = _T('_');
                    else
                        diskName[disk][i] = instance[i];
                }

                diskName[disk][strSize] = _T('\0');

                _tcscpy(buf, perfKeys[8]);
                _tcscat(buf, instance);
                _tcscat(buf, perfKeys[9]);
                status = PdhAddCounter(query, buf, 0, &(diskReadCounter[disk]));
                diskread[disk] = 0;
                if (_GK.debug_level & DEBUG_SYSDEP)
                    printf("Adding disk %s for read monitoring with status code %i\n", buf, status);

                _tcscpy(buf, perfKeys[8]);
                _tcscat(buf, instance);
                _tcscat(buf, perfKeys[10]);
                status = PdhAddCounter(query, buf, 0, &(diskWriteCounter[disk]));
                diskwrite[disk] = 0;
                if (_GK.debug_level & DEBUG_SYSDEP)
                    printf("Adding disk %s for write monitoring with status code %i\n", buf, status);
            }

            numDisks = disk + 1;
        }
        if (objects != NULL)
            free(objects);
        if (instances != NULL)
            free(instances);
    }
#else
    HKEY startPerf;

    numDisks = 0;
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing disk key %s for read monitoring\n", perfKeys[2]);

    if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StartStat", &startPerf) == ERROR_SUCCESS) {
        // success
        dataLen = 8192;
        if (RegQueryValueEx(startPerf, perfKeys[2], NULL, &dataType, data, &dataLen) != ERROR_SUCCESS) {
            // failed
            if (_GK.debug_level & DEBUG_SYSDEP)
                printf("Initialization failed.\n");
            diskreadPerfKey = 0;
        }
        else {
            if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StatData", &diskreadPerfKey) != ERROR_SUCCESS) {
                // failed
                if (_GK.debug_level & DEBUG_SYSDEP)
                    printf("Initialization failed.\n");
                diskreadPerfKey = 0;
            }
        }
        RegCloseKey(startPerf);
    }
    else {
        // failed
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Initialization failed.\n");
        diskreadPerfKey = 0;
    }

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing disk key %s for write monitoring\n", perfKeys[3]);

    if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StartStat", &startPerf) == ERROR_SUCCESS) {
        // success
        dataLen = 8192;
        if (RegQueryValueEx(startPerf, perfKeys[3], NULL, &dataType, data, &dataLen) != ERROR_SUCCESS) {
            // failed
            if (_GK.debug_level & DEBUG_SYSDEP)
                printf("Initialization failed.\n");
            diskwritePerfKey = 0;
        }
        else {
            if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StatData", &diskwritePerfKey) != ERROR_SUCCESS) {
                // failed
                if (_GK.debug_level & DEBUG_SYSDEP)
                    printf("Initialization failed.\n");
                diskwritePerfKey = 0;
            }
            else {
                numDisks = 1;
                _tcscpy(diskName[0], _T("Total"));
            }
        }
        RegCloseKey(startPerf);
    }
    else {
        // failed
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Initialization failed.\n");
        diskwritePerfKey = 0;
    }

#endif

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Found %i disks for monitoring.\n", numDisks);

    if (numDisks == 0)
        return FALSE;
    else
        return TRUE;
	}

void win32_sys_disk_stop(void)
{
#if !defined(USE_PDH)
    HKEY startPerf;

    if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StopStat", &startPerf) == ERROR_SUCCESS) {
        // success
        dataLen = 8192;
        RegQueryValueEx(startPerf, perfKeys[2], NULL, &dataType, data, &dataLen);
        cpuperfKey = 0;
        RegCloseKey(startPerf);
    }
    if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StopStat", &startPerf) == ERROR_SUCCESS) {
        // success
        dataLen = 8192;
        RegQueryValueEx(startPerf, perfKeys[3], NULL, &dataType, data, &dataLen);
        cpuperfKey = 0;
        RegCloseKey(startPerf);
    }
#endif
}

/* ===================================================================== */
/* Proc monitor interface */
/* ===================================================================== */

void gkrellm_sys_proc_read_data(void)
	{
#if defined(USE_PDH)
    DWORD type;
    PDH_FMT_COUNTERVALUE value;
#else
    DWORD* value;
    HANDLE hSnap;
#endif
    gint	n_running = 0, n_processes = 0;
	gulong	n_forks = 0;
    static gulong last_n_forks = 0;
    gulong new_forks;
	static gfloat	fload = 0;
    gfloat a;

    win32_read_proc_stat();

#if defined(USE_PDH)
    if (winVer > 0 && query != 0) {
        status = PdhGetFormattedCounterValue(processCounter, PDH_FMT_LONG, &type, &value);
        n_processes = value.longValue;

        status = PdhGetFormattedCounterValue(threadCounter, PDH_FMT_LONG, &type, &value);
        n_forks = value.longValue;
    }
#else
    if (threadPerfKey != 0) {
        dataLen = 8192;
        RegQueryValueEx(threadPerfKey, perfKeys[1], NULL, &dataType, data, &dataLen);
        if (dataType == REG_BINARY) {
            value = data;
            n_forks = *value;
        }
    }

    hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnap != -1) {
        PROCESSENTRY32 entry;

        entry.dwSize = sizeof(PROCESSENTRY32);
        n_processes = 1;
        Process32First(hSnap, &entry);

        while (Process32Next(hSnap, &entry) == TRUE)
            ++n_processes;

        CloseHandle(hSnap);
    }
    else
        n_processes = n_forks;

#endif

    n_running = n_processes;

    //fload - is the system load average, an exponential moving average over a period
    //    of a minute of n_running.  It measures how heavily a system is loaded
    //    with processes or threads competing for cpu time slices.

    //All the unix OSs have a system call for getting the load average.  But if
    //you don't and can get a n_running number, you can calculate fload.  An
    //exponential moving average (ema) is done like:

    //    a = 2 / (period + 1)
    //    ema = ema + a * (new_value - ema)


    a = 2. / ((_GK.update_HZ * 60.) + 1.);
    new_forks = n_forks - last_n_forks;
    if (new_forks < 0)
        new_forks = 0;
    fload = fload + a * (new_forks - fload);
    if (fload < 0) 
        fload = 0;

	gkrellm_proc_assign_data(n_processes, n_running, n_forks, fload);

    last_n_forks = n_forks;
	}

void gkrellm_sys_proc_read_users(void)
	{
	static time_t	utmp_mtime;
	gint			n_users = 0;
#if defined(USE_PDH)
    DWORD t1, t2;
    LPWKSTA_USER_INFO_0 ptr = 0;
    NET_API_STATUS err;

    err = NetWkstaUserEnum(NULL, 0, (LPBYTE*) &ptr, 1024, &t1, &t2, NULL);
    if (err != 0 && err != ERROR_MORE_DATA)
    	n_users = 1;
    else
        n_users = t2;
    NetApiBufferFree(ptr);
#else
        n_users = 1;
#endif

	gkrellm_proc_assign_users(n_users);

	}

gboolean gkrellm_sys_proc_init(void)
{
    HKEY startPerf;

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing process monitor.\n");

#if defined(USE_PDH)
    if (winVer > 0 && query != 0) {
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Adding %s as process monitor.\n", perfKeys[2]);
        status = PdhAddCounter(query, perfKeys[2], 0, &processCounter);
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Adding %s as thread monitor.\n", perfKeys[3]);
        status = PdhAddCounter(query, perfKeys[3], 0, &threadCounter);
    }
#else
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing %s as thread and process monitor.\n", perfKeys[1]);
    if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StartStat", &startPerf) == ERROR_SUCCESS) {
        // success
        dataLen = 8192;
        if (RegQueryValueEx(startPerf, perfKeys[1], NULL, &dataType, data, &dataLen) != ERROR_SUCCESS) {
            // failed
            if (_GK.debug_level & DEBUG_SYSDEP)
                printf("Initialization failed.\n");
            threadPerfKey = 0;
        }
        else {
            if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StatData", &threadPerfKey) != ERROR_SUCCESS) {
                // failed
                if (_GK.debug_level & DEBUG_SYSDEP)
                    printf("Initialization failed.\n");
                threadPerfKey = 0;
            }
        }
        RegCloseKey(startPerf);
    }
    else {
        // failed
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Initialization failed.\n");
        threadPerfKey = 0;
    }
#endif

	return TRUE;
	}

void win32_sys_process_stop(void)
{
#if !defined(USE_PDH)
    HKEY startPerf;

    if (RegOpenKey(HKEY_DYN_DATA, "PerfStats\\StopStat", &startPerf) == ERROR_SUCCESS) {
        // success
        dataLen = 8192;
        RegQueryValueEx(startPerf, perfKeys[1], NULL, &dataType, data, &dataLen);
        threadPerfKey = 0;
        RegCloseKey(startPerf);
    }
#endif
}


/* ===================================================================== */
/* Memory/Swap monitor interface */
/* ===================================================================== */

void gkrellm_sys_mem_read_data(void)
{
	gboolean	using_tagged = FALSE;
	guint64		total, used, x_used, free, shared, buffers;
    MEMORYSTATUS memStatus;
    long pageAvail, pageTot;
    long physAvail, physTot;
    long virtAvail, virtTot;
    long len;
    long load;
    long cached;

    GlobalMemoryStatus(&memStatus);

    pageAvail = memStatus.dwAvailPageFile;
    physAvail = memStatus.dwAvailPhys;
    virtAvail = memStatus.dwAvailVirtual;
    len = memStatus.dwLength;
    load = memStatus.dwMemoryLoad;
    physTot = memStatus.dwTotalPhys;
    pageTot = memStatus.dwTotalPageFile;
    virtTot = memStatus.dwTotalVirtual;

    total = physTot;
    free = physAvail; 

    // don't know how to get at these
    shared = 0;
    buffers = 0;

    // don't know if this is correct
    cached = (virtTot - virtAvail) - (pageTot - pageAvail);
    if (cached < 0) {
        cached = 0;
    }

    x_used = total - free;

	used = x_used - buffers - cached;
	gkrellm_mem_assign_data(total, used, free, shared, buffers, cached);
}

void gkrellm_sys_swap_read_data(void)
{
    MEMORYSTATUS memStatus;
    long pageAvail, pageTot;
    long physAvail, physTot;
    long virtAvail, virtTot;
    long swapAvail, swapTot;
    long len;
    long load;
    long used;

    GlobalMemoryStatus(&memStatus);

    pageAvail = memStatus.dwAvailPageFile;
    physAvail = memStatus.dwAvailPhys;
    virtAvail = memStatus.dwAvailVirtual;
    len = memStatus.dwLength;
    load = memStatus.dwMemoryLoad;
    physTot = memStatus.dwTotalPhys;
    pageTot = memStatus.dwTotalPageFile;
    virtTot = memStatus.dwTotalVirtual;

    // what are these?
    swapin = 0;
    swapout = 0;

    // is this right?
    swapTot = pageTot - physTot;
    swapAvail = pageAvail - physAvail;

    used = swapTot - swapAvail;
    if (used < 0)
        used = 0;

    gkrellm_swap_assign_data(swapTot, used, swapin, swapout);
}

gboolean gkrellm_sys_mem_init(void)
{
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initialized Memory monitor.\n");

	return TRUE;
}


/* ===================================================================== */
/* Battery monitor interface */
/* ===================================================================== */

#define	L_NO_BATTERY	0x80
#define	L_ON_LINE		1
#define	L_CHARGING		8
#define L_UNKNOWN		0xFF

void gkrellm_sys_battery_read_data(void)
{
	gboolean	available, on_line, charging;
	gint		percent, time_left;
	gchar		units[32];

    SYSTEM_POWER_STATUS power;

    GetSystemPowerStatus(&power);

	if ((power.BatteryFlag & L_NO_BATTERY) == L_NO_BATTERY || (power.BatteryFlag & L_UNKNOWN) == L_UNKNOWN)
		available = FALSE;
	else
		available = TRUE;

	on_line = ((power.ACLineStatus & L_ON_LINE) == L_ON_LINE) ? TRUE : FALSE;
	charging= ((power.BatteryFlag & L_CHARGING) == L_CHARGING) ? TRUE : FALSE;

    time_left = power.BatteryLifeTime;
	if (!strcmp(units, "sec"))
		time_left /= 60;

    percent = power.BatteryLifePercent;

	gkrellm_battery_assign_data(0, available, on_line, charging,
					percent, time_left);
}

gboolean gkrellm_sys_battery_init()
{
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initialized Battery monitor.\n");

	return TRUE;
}


/* ============================================== */
/* FS monitor interfaces */
/* ===================================================================== */

gboolean gkrellm_sys_fs_fstab_modified(void)
{
    return FALSE;
}

void eject_win32_cdrom(gchar *device)
{
    HANDLE hFile;
    BOOL err;
    char buf[25];
    DWORD numBytes;

    if (strlen(device) <= 0)
        return;

    sprintf(buf, "\\\\.\\%c:", device[0]);
    hFile = CreateFile(buf, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, NULL);

    if (hFile != 0 && hFile != INVALID_HANDLE_VALUE) {
        // this should be safe for non-removable drives
        err = DeviceIoControl(hFile, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &numBytes, NULL);
        err = DeviceIoControl(hFile, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &numBytes, NULL);

        CloseHandle(hFile);
    }
}

gboolean gkrellm_sys_fs_init(void) 
{
    gkrellm_fs_mounting_unsupported();

    gkrellm_fs_setup_eject(NULL, NULL, eject_win32_cdrom, NULL);

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing file system monitor.\n");

    return TRUE;
}

void gkrellm_sys_fs_get_fsusage(gpointer fs, gchar *dir)
{
	//gkrellm_fs_assign_fsusage_data(fs,
	//			(gulong) st.f_blocks, (gulong) st.f_bavail,
	//			(gulong) st.f_bfree, (gulong) st.f_bsize);

    BOOL err = 0;
    ULARGE_INTEGER freeAvailableToCaller;
    ULARGE_INTEGER totalBytes;
    ULARGE_INTEGER freeBytes;
    gulong total, freeCaller, free;
    gulong secPerCluster, bytesPerSector, freeClusters, totalClusters;
    HANDLE hFile;
    char buf[1024];

    if (strlen(dir) <= 0)
        return;

    if (winVer > 0) {
        sprintf(buf, "\\\\.\\%c:", dir[0]);

        hFile = CreateFile(buf, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, NULL);

        if (hFile == 0 || hFile == INVALID_HANDLE_VALUE) {
            return;
        }
        CloseHandle(hFile);
    }

    if (fswin95 == 1) {
        err = GetDiskFreeSpaceEx(dir, &freeAvailableToCaller, &totalBytes, &freeBytes);

        if (err != 0) {
            total = EnlargedUnsignedDivide(totalBytes, 1024, 0);
            freeCaller = EnlargedUnsignedDivide(freeAvailableToCaller, 1024, 0);
            free = EnlargedUnsignedDivide(freeBytes, 1024, 0);

            // fs, blocks, avail, free, size
	        gkrellm_fs_assign_fsusage_data(fs, total, freeCaller, free, 1024);
        }
    }
    if (err == 0 && fswin95 == 0) {
        err = GetDiskFreeSpace(dir, &secPerCluster, &bytesPerSector, &freeClusters, &totalClusters);
        if (err != 0) {
            gkrellm_fs_assign_fsusage_data(fs, totalClusters * secPerCluster, freeClusters * secPerCluster, freeClusters * secPerCluster, bytesPerSector);
        }
    }

}

void gkrellm_sys_fs_get_mounts_list(void)
{
    char buf[1024];
    char* drive;

    GetLogicalDriveStrings(1024, buf);
    for (drive = buf; *drive != 0; drive += lstrlen(drive) + 1) {
        // dir, dev, type
        if (strcmp("A:\\", drive) != 0 && strcmp("a:\\", drive) != 0 && strcmp("B:\\", drive) != 0 && strcmp("b:\\", drive) != 0) {
    	    gkrellm_fs_add_to_mounts_list(drive, drive, "");
        }
    }    
}

void gkrellm_sys_fs_get_fstab_list(void)
{
    char buf[1024];
    char* drive;

    GetLogicalDriveStrings(1024, buf);
    for (drive = buf; *drive != 0; drive += lstrlen(drive) + 1) {
        // dir, dev, type, opt
        if (strcmp("A:\\", drive) != 0 && strcmp("a:\\", drive) != 0 && strcmp("B:\\", drive) != 0 && strcmp("b:\\", drive) != 0) {
            if (_GK.debug_level & DEBUG_SYSDEP)
                printf("Adding fstab %s\n", drive);
            gkrellm_fs_add_to_fstab_list(drive, drive, "", "");
        }
    }    
}

/* ================================================ */
/* INET monitor interfaces */
/* ===================================================================== */

gboolean gkrellm_sys_inet_init(void)
{
    //return TRUE;
    return FALSE;
}

void gkrellm_sys_inet_read_tcp_data(void)
{
//    FILE		*f;
//	ActiveTCP	tcp;
//	gchar		buf[512];
//	gint		tcp_status;
//	gulong		addr;

    //what address should this be?
    // this is an in_addr
    // what ports?
//    tcp.remote_addr.s_addr = inet_addr("127.0.0.1");
//    tcp.family = AF_INET;
//    tcp.remote_port = 80;
//    tcp.local_port = 80;

//    gkrellm_inet_log_tcp_port_data(&tcp);

/*	if ((f = fopen(PROC_NET_TCP_FILE, "r")) != NULL)
		{
		fgets(buf, sizeof(buf), f);		

		while (fgets(buf, sizeof(buf), f))
			{
			sscanf(buf, "%*d: %*x:%x %lx:%x %x", &tcp.local_port,
						&addr, &tcp.remote_port, &tcp_status);
			tcp.remote_addr.s_addr = (uint32_t) addr;
			tcp.family = AF_INET;
			if (tcp_status != TCP_ALIVE)
				continue;
			gkrellm_inet_log_tcp_port_data(&tcp);
			}
		fclose(f);
		}
*/
}



/* ===================================================================== */
/* Uptime monitor interface */
/* ===================================================================== */

time_t gkrellm_sys_uptime_read_uptime(void)
{
#if defined(USE_PDH)
    DWORD type;
    PDH_FMT_COUNTERVALUE value;
#endif
    long l = 0;

    win32_read_proc_stat();

#if defined(USE_PDH)
    if (winVer > 0 && query != 0) {
        status = PdhGetFormattedCounterValue(uptimeCounter, PDH_FMT_LONG, &type, &value);
        l = value.longValue;
    }
#else
    l = GetTickCount() / 1000;
#endif

    return (time_t) l;
    }

gboolean gkrellm_sys_uptime_init(void)
{
#if defined(USE_PDH)
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Initializing uptime monitor.\n");

    if (winVer > 0 && query != 0) {
        status = PdhAddCounter(query, perfKeys[4], 0, &uptimeCounter);
    }
#endif

    return TRUE;
    }

/* ===================================================================== */
/* hostname interface */
/* ===================================================================== */

gchar *gkrellm_sys_get_host_name(void)
{
	static gboolean	have_it = FALSE;
    char buf2[128];
    int err;

    if (!have_it) {
        err = gethostname(buf2, 128);

        if (err != 0) {
            hostname = g_strdup("Unknown");
        }
        else {
            hostname = g_strdup(buf2);
        }

        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Retrieving host name.\n");

        have_it = TRUE;
    }

	return hostname;
}

/* ===================================================================== */
/* System name interface */
/* ===================================================================== */

gchar *gkrellm_sys_get_system_name(void)
{
	static gboolean	have_it = FALSE;
    char osName[255];
    char build[255];

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Retrieving system name.\n");

    if (!have_it) {
        strcpy(osName, "Unknown");
        strcpy(build, "");

        if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
            if (info.dwMinorVersion == 0) {
                strcpy(osName, "Win 95");
            }
            if (info.dwMinorVersion == 10) {
                strcpy(osName, "Win 98");
            }
            if (info.dwMinorVersion == 90) {
                strcpy(osName, "Win Me");
            }
    //        ss << " (" << LOWORD(build) << ")";
    //        if (CSDVersion.size() > 0) {
    //            ss << CSDVersion[0];
    //        }
        }
        else {
            if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) {
                if (info.dwMinorVersion == 0) {
                    if (info.dwMajorVersion == 5) {
                        strcpy(osName, "Win 2000");
                    }
                    if (info.dwMajorVersion == 4) {
                        strcpy(osName, "Win NT 4.0");
                    }
                }
                if (info.dwMinorVersion == 51) {
                    strcpy(osName, "Win NT 3.51");
                }
                if (info.dwMinorVersion == 1) {
                    strcpy(osName, "Win XP");
                }
    //            ss << " (" << build << ")";
            }
        }

        sname = g_strdup(osName);
        have_it = TRUE;
    	//sname = g_strdup_printf("%s %s", osName, "");
    }


    return sname;
}

/* ===================================================================== */
/* Misc functions */
/* ===================================================================== */

void readPerfKeyFile(void)
{
    FILE* fp;
    gchar *fileName;
    TCHAR buf[1024];
    TCHAR key[1024];
    TCHAR* s;
    int pos;

    // load defaults in case file is not there or missing entries
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Beginning read of perfkey.dat\n");
#if defined(USE_PDH)
    // \\Processor(0)
    placePerfKey("processor_time", _T(")\\% Processor Time"));
    placePerfKey("privileged_time", _T(")\\% Privileged Time"));
    placePerfKey("processes", _T("\\System\\Processes"));
    placePerfKey("forks", _T("\\System\\Threads"));
    placePerfKey("uptime", _T("\\System\\System Up Time"));
    placePerfKey("netpart1", _T("\\Network Interface("));
    placePerfKey("netreceive", _T(")\\Bytes Received/sec"));
    placePerfKey("netsend", _T(")\\Bytes Sent/sec"));
    placePerfKey("diskpart1", _T("\\PhysicalDisk("));
    placePerfKey("diskread", _T(")\\Disk Read Bytes/sec"));
    placePerfKey("diskwrite", _T(")\\Disk Write Bytes/sec"));
    placePerfKey("processor_start", _T("\\Processor("));
#else
    placePerfKey("cpuusage", _T("KERNEL\\CPUUsage"));
    placePerfKey("threads", _T("KERNEL\\Threads"));
    placePerfKey("breads", _T("VFAT\\BReadsSec"));
    placePerfKey("bwrites", _T("VFAT\\BWritesSec"));
    placePerfKey("bnetwork", _T("Network Monitor\\0000 - Ethernet bytes/second"));
    placePerfKey("netname", _T("Ether"));
#endif

    // now try to read translated keys from file
    fileName = g_strdup_printf("%s\\%s\\perfkey.dat", gkrellm_homedir(), GKRELLM_DIR);
    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Looking for perfkey.dat at %s\n", fileName);

    fp = fopen(fileName, "r");
    if (fp) {
		while ((fgets(buf, sizeof(buf), fp)) != NULL) {
			if ((s = _tcschr(buf, _T('\n'))) != NULL)
				*s = _T('\0');

            if (_tcsclen(buf) > 0 && buf[0] != _T(';')) {
                if ((s = _tcschr(buf, _T(' '))) != NULL) {
                    if (_tcsclen(s) > 0) {
                        ++s;
                        pos = s - buf - 1;

                        if (pos > 0) {
                            _tcsncpy(key, buf, pos);
                            key[pos] = _T('\0');

                            placePerfKey(key, s);
                        }
                    }
                }
            }
        }
        fclose(fp);
    }
    else {
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Opening perfkey.dat failed, using English defaults.\n");
    }

    g_free(fileName);
}

void placePerfKey(const TCHAR* key, const TCHAR* item)
{
    int place = -1;
    int size;

#if defined(USE_PDH)
    if (_tcscmp(key, _T("processor_time")) == 0)
        place = 0;
    if (_tcscmp(key, _T("privileged_time")) == 0)
        place = 1;
    if (_tcscmp(key, _T("processes")) == 0)
        place = 2;
    if (_tcscmp(key, _T("forks")) == 0)
        place = 3;
    if (_tcscmp(key, _T("uptime")) == 0)
        place = 4;
    if (_tcscmp(key, _T("netpart1")) == 0)
        place = 5;
    if (_tcscmp(key, _T("netreceive")) == 0)
        place = 6;
    if (_tcscmp(key, _T("netsend")) == 0)
        place = 7;
    if (_tcscmp(key, _T("diskpart1")) == 0)
        place = 8;
    if (_tcscmp(key, _T("diskread")) == 0)
        place = 9;
    if (_tcscmp(key, _T("diskwrite")) == 0)
        place = 10;
    if (_tcscmp(key, _T("processor_start")) == 0)
        place = 11;
#else
    if (_tcscmp(key, _T("cpuusage")) == 0)
        place = 0;
    if (_tcscmp(key, _T("threads")) == 0)
        place = 1;
    if (_tcscmp(key, _T("breads")) == 0)
        place = 2;
    if (_tcscmp(key, _T("bwrites")) == 0)
        place = 3;
    if (_tcscmp(key, _T("bnetwork")) == 0)
        place = 4;
    if (_tcscmp(key, _T("netname")) == 0)
        place = 5;
#endif

    if (_GK.debug_level & DEBUG_SYSDEP)
        printf("Found place %i for key %s\n", place, key);

    if (place != -1 && place < numPerfKeys) {
        free(perfKeys[place]);
        size = _tcsclen(item);
        perfKeys[place] = malloc(sizeof(TCHAR) * size + 1);
        _tcscpy(perfKeys[place], item);
        
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Putting item %s in key %s\n", item, key);
    }
    else {
        if (_GK.debug_level & DEBUG_SYSDEP)
            printf("Invalid placement for key %s and item %s\n", key, item);
    }
}

int getuid()
{
    // gets user id - doesn't exist for Windows
    return 0;
}

void fcntl(int fd, int cmd, ...)
{
    // Windows can't lock files that I know of
}

void fchmod(int fd, mode_t mode)
{
    // no way to set permissions on Windows
}

//int getpid()
//{
//    return 0;
//}

void win32_init_callbacks(void)
{
#if defined(WIN32_CLIENT)
      // Data structure allocation
    plugin_callbacks.gkrellm_chart_new0 = gkrellm_chart_new0;
    plugin_callbacks.gkrellm_chartconfig_new0 = gkrellm_chartconfig_new0;
    plugin_callbacks.gkrellm_panel_new0 = gkrellm_panel_new0;
    plugin_callbacks.gkrellm_krell_new0 = gkrellm_krell_new0;
    plugin_callbacks.gkrellm_decal_new0 = gkrellm_decal_new0;
    plugin_callbacks.gkrellm_label_new0 = gkrellm_label_new0;
    plugin_callbacks.gkrellm_style_new0 = gkrellm_style_new0;
    plugin_callbacks.gkrellm_copy_style = gkrellm_copy_style;
    plugin_callbacks.gkrellm_copy_style_values = gkrellm_copy_style_values;
    plugin_callbacks.gkrellm_textstyle_new0 = gkrellm_textstyle_new0;
    plugin_callbacks.gkrellm_copy_textstyle = gkrellm_copy_textstyle;

    // Chart functions
    plugin_callbacks.gkrellm_chart_create = gkrellm_chart_create;
    plugin_callbacks.gkrellm_chart_destroy = gkrellm_chart_destroy;
    plugin_callbacks.gkrellm_chart_hide = gkrellm_chart_hide;
    plugin_callbacks.gkrellm_chart_show = gkrellm_chart_show;
    plugin_callbacks.gkrellm_chart_enable_visibility = gkrellm_chart_enable_visibility;
    plugin_callbacks.gkrellm_is_chart_visible = gkrellm_is_chart_visible;
    plugin_callbacks.gkrellm_set_draw_chart_function = gkrellm_set_draw_chart_function;
    plugin_callbacks.gkrellm_draw_chart_to_screen = gkrellm_draw_chart_to_screen;
    plugin_callbacks.gkrellm_draw_chart_label = gkrellm_draw_chart_label;
    plugin_callbacks.gkrellm_draw_chart_text = gkrellm_draw_chart_text;
    plugin_callbacks.gkrellm_reset_chart = gkrellm_reset_chart;
    plugin_callbacks.gkrellm_reset_and_draw_chart = gkrellm_reset_and_draw_chart;
    plugin_callbacks.gkrellm_refresh_chart = gkrellm_refresh_chart;
    plugin_callbacks.gkrellm_rescale_chart = gkrellm_rescale_chart;
    plugin_callbacks.gkrellm_clear_chart = gkrellm_clear_chart;
    plugin_callbacks.gkrellm_clear_chart_pixmap = gkrellm_clear_chart_pixmap;
    plugin_callbacks.gkrellm_clean_bg_src_pixmap = gkrellm_clean_bg_src_pixmap;
    plugin_callbacks.gkrellm_draw_chart_grid_line = gkrellm_draw_chart_grid_line;
    plugin_callbacks.gkrellm_chart_bg_piximage_override = gkrellm_chart_bg_piximage_override;
    plugin_callbacks.gkrellm_chart_width = gkrellm_chart_width;
    plugin_callbacks.gkrellm_set_chart_height_default = gkrellm_set_chart_height_default;
    plugin_callbacks.gkrellm_set_chart_height = gkrellm_set_chart_height;
    plugin_callbacks.gkrellm_get_chart_scalemax = gkrellm_get_chart_scalemax;
    plugin_callbacks.gkrellm_render_data_pixmap = gkrellm_render_data_pixmap;
    plugin_callbacks.gkrellm_render_data_grid_pixmap = gkrellm_render_data_grid_pixmap;

      // ChartData functions
    plugin_callbacks.gkrellm_add_chartdata = gkrellm_add_chartdata;
    plugin_callbacks.gkrellm_add_default_chartdata = gkrellm_add_default_chartdata;
    plugin_callbacks.gkrellm_alloc_chartdata = gkrellm_alloc_chartdata;
    plugin_callbacks.gkrellm_store_chartdata = gkrellm_store_chartdata;
    plugin_callbacks.gkrellm_draw_chartdata = gkrellm_draw_chartdata;
    plugin_callbacks.gkrellm_monotonic_chartdata = gkrellm_monotonic_chartdata;
    plugin_callbacks.gkrellm_get_chartdata_hide = gkrellm_get_chartdata_hide;
    plugin_callbacks.gkrellm_get_current_chartdata = gkrellm_get_current_chartdata;
    plugin_callbacks.gkrellm_get_chartdata_data = gkrellm_get_chartdata_data;
    plugin_callbacks.gkrellm_set_chartdata_draw_style = gkrellm_set_chartdata_draw_style;
    plugin_callbacks.gkrellm_set_chartdata_draw_style_default = gkrellm_set_chartdata_draw_style_default;
    plugin_callbacks.gkrellm_set_chartdata_flags = gkrellm_set_chartdata_flags;
    plugin_callbacks.gkrellm_scale_chartdata = gkrellm_scale_chartdata;

      // ChartConfig functions
    plugin_callbacks.gkrellm_chartconfig_window_create = gkrellm_chartconfig_window_create;
    plugin_callbacks.gkrellm_chartconfig_window_destroy = gkrellm_chartconfig_window_destroy;
    plugin_callbacks.gkrellm_chartconfig_grid_resolution_adjustment = gkrellm_chartconfig_grid_resolution_adjustment;
    plugin_callbacks.gkrellm_set_chartconfig_grid_resolution = gkrellm_set_chartconfig_grid_resolution;
    plugin_callbacks.gkrellm_get_chartconfig_grid_resolution = gkrellm_get_chartconfig_grid_resolution;
    plugin_callbacks.gkrellm_chartconfig_grid_resolution_connect = gkrellm_chartconfig_grid_resolution_connect;
    plugin_callbacks.gkrellm_set_chartconfig_flags = gkrellm_set_chartconfig_flags;
    plugin_callbacks.gkrellm_chartconfig_grid_resolution_label = gkrellm_chartconfig_grid_resolution_label;
    plugin_callbacks.gkrellm_set_chartconfig_auto_grid_resolution = gkrellm_set_chartconfig_auto_grid_resolution;
    plugin_callbacks.gkrellm_set_chartconfig_auto_resolution_stick = gkrellm_set_chartconfig_auto_resolution_stick;
    plugin_callbacks.gkrellm_set_chartconfig_sequence_125 = gkrellm_set_chartconfig_sequence_125;
    plugin_callbacks.gkrellm_set_chartconfig_fixed_grids = gkrellm_set_chartconfig_fixed_grids;
    plugin_callbacks.gkrellm_get_chartconfig_fixed_grids = gkrellm_get_chartconfig_fixed_grids;
    plugin_callbacks.gkrellm_chartconfig_fixed_grids_connect = gkrellm_chartconfig_fixed_grids_connect;
    plugin_callbacks.gkrellm_get_chartconfig_height = gkrellm_get_chartconfig_height;
    plugin_callbacks.gkrellm_chartconfig_height_connect = gkrellm_chartconfig_height_connect;
    plugin_callbacks.gkrellm_save_chartconfig = gkrellm_save_chartconfig;
    plugin_callbacks.gkrellm_load_chartconfig = gkrellm_load_chartconfig;
    plugin_callbacks.gkrellm_chartconfig_destroy = gkrellm_chartconfig_destroy;

      // Panel functions
    plugin_callbacks.gkrellm_panel_configure = gkrellm_panel_configure;
    plugin_callbacks.gkrellm_panel_configure_add_height = gkrellm_panel_configure_add_height;
    plugin_callbacks.gkrellm_panel_configure_set_height = gkrellm_panel_configure_set_height;
    plugin_callbacks.gkrellm_panel_configure_get_height = gkrellm_panel_configure_get_height;
    plugin_callbacks.gkrellm_panel_create = gkrellm_panel_create;
    plugin_callbacks.gkrellm_panel_destroy = gkrellm_panel_destroy;
    plugin_callbacks.gkrellm_panel_hide = gkrellm_panel_hide;
    plugin_callbacks.gkrellm_panel_show = gkrellm_panel_show;
    plugin_callbacks.gkrellm_panel_enable_visibility = gkrellm_panel_enable_visibility;
    plugin_callbacks.gkrellm_is_panel_visible = gkrellm_is_panel_visible;
    plugin_callbacks.gkrellm_panel_keep_lists = gkrellm_panel_keep_lists;
    plugin_callbacks.gkrellm_draw_panel_label = gkrellm_draw_panel_label;
    plugin_callbacks.gkrellm_draw_panel_layers = gkrellm_draw_panel_layers;
    plugin_callbacks.gkrellm_draw_panel_layers_force = gkrellm_draw_panel_layers_force;
    plugin_callbacks.gkrellm_panel_bg_piximage_override = gkrellm_panel_bg_piximage_override;

      // Krell functions
    plugin_callbacks.gkrellm_create_krell = gkrellm_create_krell;
    plugin_callbacks.gkrellm_set_krell_full_scale = gkrellm_set_krell_full_scale;
    plugin_callbacks.gkrellm_set_style_krell_values = gkrellm_set_style_krell_values;
    plugin_callbacks.gkrellm_set_style_krell_values_default = gkrellm_set_style_krell_values_default;
    plugin_callbacks.gkrellm_set_style_slider_values_default = gkrellm_set_style_slider_values_default;
    plugin_callbacks.gkrellm_set_krell_margins = gkrellm_set_krell_margins;
    plugin_callbacks.gkrellm_set_krell_expand = gkrellm_set_krell_expand;
    plugin_callbacks.gkrellm_update_krell = gkrellm_update_krell;
    plugin_callbacks.gkrellm_monotonic_krell_values = gkrellm_monotonic_krell_values;
    plugin_callbacks.gkrellm_destroy_krell_list = gkrellm_destroy_krell_list;
    plugin_callbacks.gkrellm_destroy_krell = gkrellm_destroy_krell;
    plugin_callbacks.gkrellm_move_krell_yoff = gkrellm_move_krell_yoff;
    plugin_callbacks.gkrellm_remove_krell = gkrellm_remove_krell;
    plugin_callbacks.gkrellm_insert_krell = gkrellm_insert_krell;
    plugin_callbacks.gkrellm_insert_krell_nth = gkrellm_insert_krell_nth;

      // Decal and Decalbutton functions
    plugin_callbacks.gkrellm_create_decal_text = gkrellm_create_decal_text;
    plugin_callbacks.gkrellm_create_decal_pixmap = gkrellm_create_decal_pixmap;
    plugin_callbacks.gkrellm_draw_decal_pixmap = gkrellm_draw_decal_pixmap;
    plugin_callbacks.gkrellm_draw_decal_text = gkrellm_draw_decal_text;
    plugin_callbacks.gkrellm_draw_decal_on_chart = gkrellm_draw_decal_on_chart;
    plugin_callbacks.gkrellm_move_decal = gkrellm_move_decal;
    plugin_callbacks.gkrellm_decal_on_top_layer = gkrellm_decal_on_top_layer;
    plugin_callbacks.gkrellm_destroy_decal = gkrellm_destroy_decal;
    plugin_callbacks.gkrellm_make_decal_visible = gkrellm_make_decal_visible;
    plugin_callbacks.gkrellm_make_decal_invisible = gkrellm_make_decal_invisible;
    plugin_callbacks.gkrellm_is_decal_visible = gkrellm_is_decal_visible;
    plugin_callbacks.gkrellm_remove_decal = gkrellm_remove_decal;
    plugin_callbacks.gkrellm_insert_decal = gkrellm_insert_decal;
    plugin_callbacks.gkrellm_insert_decal_nth = gkrellm_insert_decal_nth;
    plugin_callbacks.gkrellm_destroy_decal_list = gkrellm_destroy_decal_list;
    plugin_callbacks.gkrellm_set_decal_button_index = gkrellm_set_decal_button_index;
    plugin_callbacks.gkrellm_make_decal_button = gkrellm_make_decal_button;
    plugin_callbacks.gkrellm_make_overlay_button = gkrellm_make_overlay_button;
    plugin_callbacks.gkrellm_put_decal_in_panel_button = gkrellm_put_decal_in_panel_button;
    plugin_callbacks.gkrellm_put_decal_in_meter_button = gkrellm_put_decal_in_meter_button;
    plugin_callbacks.gkrellm_put_label_in_panel_button = gkrellm_put_label_in_panel_button;
    plugin_callbacks.gkrellm_put_label_in_meter_button = gkrellm_put_label_in_meter_button;
    plugin_callbacks.gkrellm_make_scaled_button = gkrellm_make_scaled_button;
    plugin_callbacks.gkrellm_decal_is_button = gkrellm_decal_is_button;
    plugin_callbacks.gkrellm_set_in_button_callback = gkrellm_set_in_button_callback;
    plugin_callbacks.gkrellm_in_button = gkrellm_in_button;
    plugin_callbacks.gkrellm_in_decal = gkrellm_in_decal;
    plugin_callbacks.gkrellm_decal_button_connect = gkrellm_decal_button_connect;
    plugin_callbacks.gkrellm_decal_button_right_connect = gkrellm_decal_button_right_connect;
    plugin_callbacks.gkrellm_set_button_sensitive = gkrellm_set_button_sensitive;
    plugin_callbacks.gkrellm_hide_button = gkrellm_hide_button;
    plugin_callbacks.gkrellm_show_button = gkrellm_show_button;
    plugin_callbacks.gkrellm_destroy_button = gkrellm_destroy_button;

      // Pixops
    plugin_callbacks.gkrellm_load_piximage = gkrellm_load_piximage;
    plugin_callbacks.gkrellm_piximage_new_from_file = gkrellm_piximage_new_from_file;
    plugin_callbacks.gkrellm_piximage_new_from_xpm_data = gkrellm_piximage_new_from_xpm_data;
    plugin_callbacks.gkrellm_set_piximage_border = gkrellm_set_piximage_border;
    plugin_callbacks.gkrellm_scale_pixbuf_to_pixmap = gkrellm_scale_pixbuf_to_pixmap;
    plugin_callbacks.gkrellm_scale_piximage_to_pixbuf = gkrellm_scale_piximage_to_pixbuf;
    plugin_callbacks.gkrellm_scale_piximage_to_pixmap = gkrellm_scale_piximage_to_pixmap;
    plugin_callbacks.gkrellm_paste_piximage = gkrellm_paste_piximage;
    plugin_callbacks.gkrellm_paste_pixbuf = gkrellm_paste_pixbuf;
    plugin_callbacks.gkrellm_destroy_piximage = gkrellm_destroy_piximage;
    plugin_callbacks.gkrellm_clone_piximage = gkrellm_clone_piximage;
    plugin_callbacks.gkrellm_clone_pixmap = gkrellm_clone_pixmap;
    plugin_callbacks.gkrellm_clone_bitmap = gkrellm_clone_bitmap;
    plugin_callbacks.gkrellm_free_pixmap = gkrellm_free_pixmap;
    plugin_callbacks.gkrellm_free_bitmap = gkrellm_free_bitmap;

      // Misc support functions
    plugin_callbacks.gkrellm_get_top_window = gkrellm_get_top_window;
    plugin_callbacks.gkrellm_set_gkrellmrc_piximage_border = gkrellm_set_gkrellmrc_piximage_border;
    plugin_callbacks.gkrellm_get_gkrellmrc_integer = gkrellm_get_gkrellmrc_integer;
    plugin_callbacks.gkrellm_get_gkrellmrc_string = gkrellm_get_gkrellmrc_string;
    plugin_callbacks.gkrellm_get_gkrellmrc_piximage_border = gkrellm_get_gkrellmrc_piximage_border;
    plugin_callbacks.gkrellm_freeze_side_frame_packing = gkrellm_freeze_side_frame_packing;
    plugin_callbacks.gkrellm_thaw_side_frame_packing = gkrellm_thaw_side_frame_packing;
    plugin_callbacks.gkrellm_pack_side_frames = gkrellm_pack_side_frames;
    plugin_callbacks.gkrellm_draw_string = gkrellm_draw_string;
    plugin_callbacks.gkrellm_draw_text = gkrellm_draw_text;
    plugin_callbacks.gkrellm_apply_launcher = gkrellm_apply_launcher;
    plugin_callbacks.gkrellm_setup_launcher = gkrellm_setup_launcher;
    plugin_callbacks.gkrellm_setup_decal_launcher = gkrellm_setup_decal_launcher;
    plugin_callbacks.gkrellm_configure_tooltip = gkrellm_configure_tooltip;
    plugin_callbacks.gkrellm_launch_button_cb = gkrellm_launch_button_cb;
    plugin_callbacks.gkrellm_disable_plugin_connect = gkrellm_disable_plugin_connect;
    plugin_callbacks.gkrellm_get_pid = gkrellm_get_pid;
    plugin_callbacks.gkrellm_monitor_height_adjust = gkrellm_monitor_height_adjust;
    plugin_callbacks.gkrellm_using_default_theme = gkrellm_using_default_theme;
    plugin_callbacks.gkrellm_open_config_window = gkrellm_open_config_window;
    plugin_callbacks.gkrellm_config_window_shown = gkrellm_config_window_shown;
    plugin_callbacks.gkrellm_config_modified = gkrellm_config_modified;
    plugin_callbacks.gkrellm_get_style_margins = gkrellm_get_style_margins;
    plugin_callbacks.gkrellm_set_style_margins = gkrellm_set_style_margins;
    plugin_callbacks.gkrellm_get_top_bottom_margins = gkrellm_get_top_bottom_margins;
    plugin_callbacks.gkrellm_style_is_themed = gkrellm_style_is_themed;
    plugin_callbacks.gkrellm_message_dialog = gkrellm_message_dialog;
    plugin_callbacks.gkrellm_config_message_dialog = gkrellm_config_message_dialog;
    plugin_callbacks.gkrellm_spacers_set_types = gkrellm_spacers_set_types;

      // Alerts
    plugin_callbacks.gkrellm_alert_create = gkrellm_alert_create;
    plugin_callbacks.gkrellm_alert_destroy = gkrellm_alert_destroy;
    plugin_callbacks.gkrellm_check_alert = gkrellm_check_alert;
    plugin_callbacks.gkrellm_reset_alert = gkrellm_reset_alert;
    plugin_callbacks.gkrellm_reset_panel_alerts = gkrellm_reset_panel_alerts;
    plugin_callbacks.gkrellm_freeze_alert = gkrellm_freeze_alert;
    plugin_callbacks.gkrellm_thaw_alert = gkrellm_thaw_alert;
    plugin_callbacks.gkrellm_alert_trigger_connect = gkrellm_alert_trigger_connect;
    plugin_callbacks.gkrellm_alert_stop_connect = gkrellm_alert_stop_connect;
    plugin_callbacks.gkrellm_alert_config_connect = gkrellm_alert_config_connect;
    plugin_callbacks.gkrellm_render_default_alert_decal = gkrellm_render_default_alert_decal;
    plugin_callbacks.gkrellm_alert_config_window = gkrellm_alert_config_window;
    plugin_callbacks.gkrellm_alert_window_destroy = gkrellm_alert_window_destroy;
    plugin_callbacks.gkrellm_save_alertconfig = gkrellm_save_alertconfig;
    plugin_callbacks.gkrellm_load_alertconfig = gkrellm_load_alertconfig;
    plugin_callbacks.gkrellm_alert_set_triggers = gkrellm_alert_set_triggers;

      // GKrellM Styles and Textstyles
    plugin_callbacks.gkrellm_add_chart_style = gkrellm_add_chart_style;
    plugin_callbacks.gkrellm_add_meter_style = gkrellm_add_meter_style;
    plugin_callbacks.gkrellm_lookup_chart_style_id = gkrellm_lookup_chart_style_id;
    plugin_callbacks.gkrellm_lookup_meter_style_id = gkrellm_lookup_meter_style_id;
    plugin_callbacks.gkrellm_meter_style = gkrellm_meter_style;
    plugin_callbacks.gkrellm_panel_style = gkrellm_panel_style;
    plugin_callbacks.gkrellm_chart_style = gkrellm_chart_style;
    plugin_callbacks.gkrellm_meter_style_by_name = gkrellm_meter_style_by_name;
    plugin_callbacks.gkrellm_panel_style_by_name = gkrellm_panel_style_by_name;
    plugin_callbacks.gkrellm_chart_style_by_name = gkrellm_chart_style_by_name;
    plugin_callbacks.gkrellm_krell_slider_style = gkrellm_krell_slider_style;
    plugin_callbacks.gkrellm_krell_mini_style = gkrellm_krell_mini_style;
    plugin_callbacks.gkrellm_chart_textstyle = gkrellm_chart_textstyle;
    plugin_callbacks.gkrellm_panel_textstyle = gkrellm_panel_textstyle;
    plugin_callbacks.gkrellm_meter_textstyle = gkrellm_meter_textstyle;
    plugin_callbacks.gkrellm_chart_alt_textstyle = gkrellm_chart_alt_textstyle;
    plugin_callbacks.gkrellm_panel_alt_textstyle = gkrellm_panel_alt_textstyle;
    plugin_callbacks.gkrellm_meter_alt_textstyle = gkrellm_meter_alt_textstyle;

      // Accessing GKrellM GkrellmPiximages and pixmaps
    plugin_callbacks.gkrellm_bg_chart_piximage = gkrellm_bg_chart_piximage;
    plugin_callbacks.gkrellm_bg_grid_piximage = gkrellm_bg_grid_piximage;
    plugin_callbacks.gkrellm_bg_panel_piximage = gkrellm_bg_panel_piximage;
    plugin_callbacks.gkrellm_bg_meter_piximage = gkrellm_bg_meter_piximage;
    plugin_callbacks.gkrellm_krell_panel_piximage = gkrellm_krell_panel_piximage;
    plugin_callbacks.gkrellm_krell_meter_piximage = gkrellm_krell_meter_piximage;
    plugin_callbacks.gkrellm_krell_slider_piximage = gkrellm_krell_slider_piximage;
    plugin_callbacks.gkrellm_krell_mini_piximage = gkrellm_krell_mini_piximage;
    plugin_callbacks.gkrellm_get_decal_alarm_piximage = gkrellm_get_decal_alarm_piximage;
    plugin_callbacks.gkrellm_get_decal_warn_piximage = gkrellm_get_decal_warn_piximage;
    plugin_callbacks.gkrellm_data_in_pixmap = gkrellm_data_in_pixmap;
    plugin_callbacks.gkrellm_data_in_grid_pixmap = gkrellm_data_in_grid_pixmap;
    plugin_callbacks.gkrellm_data_out_pixmap = gkrellm_data_out_pixmap;
    plugin_callbacks.gkrellm_data_out_grid_pixmap = gkrellm_data_out_grid_pixmap;
    plugin_callbacks.gkrellm_decal_misc_pixmap = gkrellm_decal_misc_pixmap;
    plugin_callbacks.gkrellm_decal_misc_mask = gkrellm_decal_misc_mask;

      // Accessing other data from the GK struct
    plugin_callbacks.gkrellm_draw_GC = gkrellm_draw_GC;
    plugin_callbacks.gkrellm_bit_GC = gkrellm_bit_GC;
    plugin_callbacks.gkrellm_default_font = gkrellm_default_font;
    plugin_callbacks.gkrellm_white_color = gkrellm_white_color;
    plugin_callbacks.gkrellm_black_color = gkrellm_black_color;
    plugin_callbacks.gkrellm_in_color = gkrellm_in_color;
    plugin_callbacks.gkrellm_out_color = gkrellm_out_color;
    plugin_callbacks.gkrellm_demo_mode = gkrellm_demo_mode;
    plugin_callbacks.gkrellm_update_HZ = gkrellm_update_HZ;
    plugin_callbacks.gkrellm_get_theme_path = gkrellm_get_theme_path;
    plugin_callbacks.gkrellm_get_timer_ticks = gkrellm_get_timer_ticks;
    plugin_callbacks.gkrellm_ticks = gkrellm_ticks;
    plugin_callbacks.gkrellm_allow_scaling = gkrellm_allow_scaling;
    plugin_callbacks.gkrellm_plugin_debug = gkrellm_plugin_debug;

      // Wrappers around gtk widget functions to provide a convenience higher level
      //  interface for creating the config pages.
    plugin_callbacks.gkrellm_gtk_notebook_page = gkrellm_gtk_notebook_page;
    plugin_callbacks.gkrellm_gtk_framed_notebook_page = gkrellm_gtk_framed_notebook_page;
    plugin_callbacks.gkrellm_gtk_launcher_table_new = gkrellm_gtk_launcher_table_new;
    plugin_callbacks.gkrellm_gtk_config_launcher = gkrellm_gtk_config_launcher;
    plugin_callbacks.gkrellm_gtk_entry_get_text = gkrellm_gtk_entry_get_text;
    plugin_callbacks.gkrellm_gtk_spin_button = gkrellm_gtk_spin_button;
    plugin_callbacks.gkrellm_gtk_check_button = gkrellm_gtk_check_button;
    plugin_callbacks.gkrellm_gtk_check_button_connected = gkrellm_gtk_check_button_connected;
    plugin_callbacks.gkrellm_gtk_button_connected = gkrellm_gtk_button_connected;
    plugin_callbacks.gkrellm_gtk_scrolled_vbox = gkrellm_gtk_scrolled_vbox;
    plugin_callbacks.gkrellm_gtk_framed_vbox = gkrellm_gtk_framed_vbox;
    plugin_callbacks.gkrellm_gtk_framed_vbox_end = gkrellm_gtk_framed_vbox_end;
    plugin_callbacks.gkrellm_gtk_scrolled_text_view = gkrellm_gtk_scrolled_text_view;
    plugin_callbacks.gkrellm_gtk_text_view_append_strings = gkrellm_gtk_text_view_append_strings;
    plugin_callbacks.gkrellm_gtk_text_view_append = gkrellm_gtk_text_view_append;

      // Some utility functions
    plugin_callbacks.gkrellm_homedir = gkrellm_homedir;
    plugin_callbacks.gkrellm_dup_string = gkrellm_dup_string;
    plugin_callbacks.gkrellm_make_config_file_name = gkrellm_make_config_file_name;
    plugin_callbacks.gkrellm_make_data_file_name = gkrellm_make_data_file_name;
    plugin_callbacks.gkrellm_get_current_time = gkrellm_get_current_time;
    plugin_callbacks.gkrellm_125_sequence = gkrellm_125_sequence;
    plugin_callbacks.gkrellm_save_all = gkrellm_save_all;

      // ------- Some builtin monitor public functions -------- 

      // Functions exported by cpu.c
    plugin_callbacks.gkrellm_smp_cpus = gkrellm_smp_cpus;
    plugin_callbacks.gkrellm_cpu_stats = gkrellm_cpu_stats;


      // Functions exported by net.c
    plugin_callbacks.gkrellm_net_routes = gkrellm_net_routes;
    plugin_callbacks.gkrellm_net_stats = gkrellm_net_stats;
    plugin_callbacks.gkrellm_net_led_positions = gkrellm_net_led_positions;


      // Functions exported by the Mail monitor - see bottom of mail.c
    plugin_callbacks.gkrellm_get_mail_mute_mode = gkrellm_get_mail_mute_mode;
    plugin_callbacks.gkrellm_add_external_mbox = gkrellm_add_external_mbox;
    plugin_callbacks.gkrellm_destroy_external_mbox = gkrellm_destroy_external_mbox;
    plugin_callbacks.gkrellm_set_external_mbox_counts = gkrellm_set_external_mbox_counts;
    plugin_callbacks.gkrellm_set_external_mbox_tooltip = gkrellm_set_external_mbox_tooltip;

      // Functions new for 2.1.1
    plugin_callbacks.gkrellm_get_theme_scale = gkrellm_get_theme_scale;
    plugin_callbacks.gkrellm_offset_chartdata = gkrellm_offset_chartdata;
    plugin_callbacks.gkrellm_make_scaled_decal_pixmap = gkrellm_make_scaled_decal_pixmap;

    // Functions new for 2.1.8
	plugin_callbacks.gkrellm_panel_label_on_top_of_decals = gkrellm_panel_label_on_top_of_decals;
	plugin_callbacks.gkrellm_alert_is_activated = gkrellm_alert_is_activated;
	plugin_callbacks.gkrellm_alert_dup = gkrellm_alert_dup;
	plugin_callbacks.gkrellm_alert_config_create_connect = gkrellm_alert_config_create_connect;
	plugin_callbacks.gkrellm_alert_command_process_connect = gkrellm_alert_command_process_connect;
	plugin_callbacks.gkrellm_alert_decal_visible = gkrellm_alert_decal_visible;
	plugin_callbacks.gkrellm_alert_set_delay = gkrellm_alert_set_delay;
	plugin_callbacks.gkrellm_alert_delay_config = gkrellm_alert_delay_config;
	plugin_callbacks.gkrellm_gtk_alert_button = gkrellm_gtk_alert_button;

      // Functions new for 2.1.9
	plugin_callbacks.gkrellm_piximage_new_from_inline = gkrellm_piximage_new_from_inline;
    plugin_callbacks.gkrellm_load_piximage_from_inline = gkrellm_load_piximage_from_inline;
	plugin_callbacks.gkrellm_alert_commands_config = gkrellm_alert_commands_config;
    plugin_callbacks.gkrellm_reset_alert_soft = gkrellm_reset_alert_soft;
    
    // Functions new for 2.1.11
	plugin_callbacks.gkrellm_decal_text_clear = gkrellm_decal_text_clear;
	plugin_callbacks.gkrellm_decal_text_insert = gkrellm_decal_text_insert;
	plugin_callbacks.gkrellm_create_decal_text_with_height = gkrellm_create_decal_text_with_height;
	plugin_callbacks.gkrellm_chartconfig_callback_block = gkrellm_chartconfig_callback_block;

      // Functions new for 2.1.16
	plugin_callbacks.gkrellm_alert_get_alert_state = gkrellm_alert_get_alert_state;
	plugin_callbacks.gkrellm_alert_plugin_add = gkrellm_alert_plugin_add;
	plugin_callbacks.gkrellm_alert_plugin_alert_connect = gkrellm_alert_plugin_alert_connect;
	plugin_callbacks.gkrellm_alert_plugin_config_connect = gkrellm_alert_plugin_config_connect;
	plugin_callbacks.gkrellm_alert_plugin_config_get_id_string = gkrellm_alert_plugin_config_get_id_string;
	plugin_callbacks.gkrellm_alert_plugin_alert_attach = gkrellm_alert_plugin_alert_attach;
	plugin_callbacks.gkrellm_alert_plugin_alert_detach = gkrellm_alert_plugin_alert_detach;
	plugin_callbacks.gkrellm_alert_plugin_get_data = gkrellm_alert_plugin_get_data;
	plugin_callbacks.gkrellm_alert_plugin_command_process = gkrellm_alert_plugin_command_process;
	plugin_callbacks.gkrellm_gtk_category_vbox = gkrellm_gtk_category_vbox;
	plugin_callbacks.gkrellm_remove_launcher = gkrellm_remove_launcher;

    callbacks = &plugin_callbacks;
#endif
}

/*
* Copyright (c) 1983, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

/*
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/

/*
* Portions Copyright (c) 1996-1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/

/* BeOS doesn't yet have it's own inet_aton and Bind won't be ported
* until R5, so this is from a Bind 8 distribution. It's currently untested.
*/

int inet_aton(const char *cp, struct in_addr *addr) {
u_long val;
int base, n;
char c;
short parts[4];
short *pp = parts;
int digit;

c = *cp;
for (;;) {
/*
* Collect number up to ``.''.
* Values are specified as for C:
* 0x=hex, 0=octal, isdigit=decimal.
*/
if (!isdigit(c))
return (0);
val = 0; base = 10; digit = 0;
if (c == '0') {
c = *++cp;
if (c == 'x' || c == 'X')
base = 16, c = *++cp;
else {
base = 8;
digit = 1 ;
}
}
for (;;) {
if (isascii(c) && isdigit(c)) {
if (base == 8 && (c == '8' || c == '9'))
return (0);
val = (val * base) + (c - '0');
c = *++cp;
digit = 1;
} else if (base == 16 && isascii(c) && isxdigit(c)) {
val = (val << 4) |
(c + 10 - (islower(c) ? 'a' : 'A'));
c = *++cp;
digit = 1;
} else
break;
}
if (c == '.') {
/*
* Internet format:
* a.b.c.d
* a.b.c (with c treated as 16 bits)
* a.b (with b treated as 24 bits)
*/
if (pp >= parts + 3 || val > 0xff)
return (0);
*pp++ = val;
c = *++cp;
} else
break;
}
/*
* Check for trailing characters.
*/
if (c != '\0' && (!isascii(c) || !isspace(c)))
return (0);
/*
* Did we get a valid digit?
*/
if (!digit)
return (0);
/*
* Concoct the address according to
* the number of parts specified.
*/
n = pp - parts + 1;
switch (n) {
case 1: /* a -- 32 bits */
break;

case 2: /* a.b -- 8.24 bits */
if (val > 0xffffff)
return (0);
val |= parts[0] << 24;
break;

case 3: /* a.b.c -- 8.8.16 bits */
if (val > 0xffff)
return (0);
val |= (parts[0] << 24) | (parts[1] << 16);
break;

case 4: /* a.b.c.d -- 8.8.8.8 bits */
if (val > 0xff)
return (0);
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
break;
}
if (addr != NULL)
addr->s_addr = htonl(val);
return (1);
}

