
/*
 * $Header: /cvs/gnome/gperfmeter/src/resources.c,v 1.36 2005/01/21 20:05:03 richb Exp $
 *
 * Copyright (c) 1990-2004 Sun Microsystems, Inc. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include "perfmeter.h"
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <string.h>
#include <pwd.h>
#include <errno.h>
#include <gnome.h>
#include <gconf/gconf-client.h>
#include <gconf/gconf-value.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

static int optTmpVal;
static GOptionContext *ctx;

/* flag added to check if we are dealing with command line options */
static gboolean cmd_line;

typedef struct _PmARStruct {
    GdkColor FgPixel;
    GdkColor BgPixel;
    PangoFont *LabelFont;
    String LocalHost;
    String LocalAddr;
    String Session;
    String IconPixmap;
    String DeadPixmap;
    String SickPixmap;
    PmString *MachineNames;
    gboolean CollectWhenIconized;
    gboolean SaveByHost;
    gboolean AutoLoadByHost;
    gboolean LoadByHost;
} PmARStruct;

static PmARStruct AR;

/* globals */

/* prototypes */
static gboolean pm_arguments_post_parse(GOptionContext *, GOptionGroup *, 
                                        gpointer, GError **);

static void InitResources(void);
static void ParseCmdOptions(int, char **);
static void PostParseOptions(void);
static void PmCheckLimits(void);
static void Usage(void);

static int save_session(GnomeClient *, gint, GnomeRestartStyle,
                        gint, GnomeInteractStyle, gint, gpointer);
static gint client_die(GnomeClient *, gpointer);

static char *color_to_string(const GdkColor *);
gchar *pm_log_create_tmp_filename(void);

gboolean opt_all, opt_panel, opt_log, opt_debug, opt_horizontal, opt_line,
         opt_solid, opt_version, opt_vertical;
gchar *opt_logfile = NULL, *opt_meter = NULL, *opt_ceiling = NULL,
      *opt_maxvalue = NULL, **opt_hostnames = NULL;
gint opt_pagelength, opt_sampletime;

static GOptionEntry entries[] =
{
  { "all", 'a', 0, G_OPTION_ARG_NONE, &opt_all, 
    N_("Display all the performance statistic meters simultaneously"), NULL },

  { "panel", 'f', 0, G_OPTION_ARG_NONE, &opt_panel, 
    N_("Display the performance meter in the front panel"), NULL },

  { "log", 'l', 0, G_OPTION_ARG_NONE, &opt_log, 
    N_("Turn statistic logging on"), NULL },

  { "logfile", 'n', 0, G_OPTION_ARG_FILENAME, &opt_logfile, 
    N_("Name of file to log statistics to"), N_("SAMPLE_FILENAME") },

  { "pagelength", 'p', 0, G_OPTION_ARG_INT, &opt_pagelength,
    N_("Page length for samples written to the log file"), N_("PAGE_LENGTH") },

  { "sampletime", 's', 0, G_OPTION_ARG_INT, &opt_sampletime,
    N_("Set the sample time interval in seconds"), N_("SAMPLE_TIME") },

  { "meter", 't', 0, G_OPTION_ARG_STRING, &opt_meter, 
    N_("Specify a meter to display"), N_("METER") },

  { "ceiling", 'C', 0, G_OPTION_ARG_STRING, &opt_ceiling, 
    N_("Set a ceiling value for a given meter"), N_("\"METER CEILING\"") },

  { "debug", 'D', 0, G_OPTION_ARG_NONE, &opt_debug, 
    N_("Turn debugging on"), NULL },

  { "horizontal", 'H', 0, G_OPTION_ARG_NONE, &opt_horizontal, 
    N_("Display meters in a horizontal orientation"), NULL },

  { "line", 'L', 0, G_OPTION_ARG_NONE, &opt_line, 
    N_("Display the meters as line strip charts"), NULL },

  { "solid", 'S', 0, G_OPTION_ARG_NONE, &opt_solid, 
    N_("Display the meters as solid line strip charts"), NULL },

  { "version", 'v', 0, G_OPTION_ARG_NONE, &opt_version, 
    N_("Show version number"), NULL },

  { "vertical", 'V', 0, G_OPTION_ARG_NONE, &opt_vertical, 
    N_("Display meters in a vertical orientation"), NULL },

  { "maxvalue", 'M', 0, G_OPTION_ARG_STRING, &opt_maxvalue, 
    N_("Set a current, minimum and maximum value for the given meter"),
    N_("\"METER CURMAX MINMAX MAXMAX\"") },

  { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_hostnames,
      "Show statistics for a specific computer", NULL },

  { NULL }
};

/* Load the application resource Data
 *
 *     1) merge the application resources with the existing X resource 
 *        database. the Application resources are "Class" resources
 *        so any users perferences will override these ...
 *     2) load any session resources specified on the command line
 *     3) initialize the data structures
 *     4) merge the resource data into the application data
 *     5) parse the command line arguments
 */

void 
PmLoadResources(int argc, char **argv)
{
    int i;
    GConfClient *client;

    cmd_line = FALSE; 

    /* Initialize the resources. */
    InitResources();

    AR.LocalHost = "localhost";
    AR.LocalAddr = "127.0.0.1";

    AR.DeadPixmap = "dead.xbm";
    AR.SickPixmap = "sick.xbm";
    AR.IconPixmap = "perfmeter.xbm";

    i = 0;

/* XXX:richb - are these the correct default values? */
    AR.CollectWhenIconized = FALSE;
    AR.AutoLoadByHost = FALSE;
    AR.SaveByHost = FALSE;
    AR.LoadByHost = FALSE;
    AR.MachineNames = NULL;

    /* set other resources */
    PM->LocalHost           = AR.LocalHost;
    PM->LocalAddr           = AR.LocalAddr;
    PM->CollectWhenIconized = AR.CollectWhenIconized;
    PM->AutoLoadByHost      = AR.AutoLoadByHost;
    PM->SaveByHost          = AR.SaveByHost;
    PM->LoadByHost          = AR.LoadByHost;

    PM->IsRemote = FALSE;
    PM->Hostname = g_strdup("localhost");
    PM->LogFilename = pm_log_create_tmp_filename();

    PM->MachineNames = AR.MachineNames;
    PM->NumMachineNames = 0;
    if (AR.MachineNames && AR.MachineNames[0]) {
        for (i = 0; AR.MachineNames[i]; i++) {
            PM->NumMachineNames++;
        }
    }
    if (PM->NumMachineNames == 0) {
        PM->MachineNames = 0;
    }
    
    /* Parse the application command line arguments */
    ParseCmdOptions(argc, argv);

    /* Get the default gconf client */
    client = gconf_client_get_default();
    gconf_client_add_dir(client, "/apps/gperfmeter",
                         GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
    PM->gconf_client = client;

    /* Save the settings for command line arguments */
    if (cmd_line) {
        PmSaveResources();
    }

    PMRESETRESOURCES(NULL);

    UI->Dsp      = GDK_DISPLAY();
    UI->RScreen  = XDefaultScreenOfDisplay(UI->Dsp);
    UI->RootWin  = RootWindowOfScreen(UI->RScreen);
    UI->ColorMap = gdk_colormap_get_system();

    PostParseOptions();

    /* Check the meter limits */
    PmCheckLimits();
  
    PmSetScaleFactors();
}


static void
PostParseOptions()
{
    gint i, ascent, descent;
    PangoContext *context;
    PangoFontDescription *desc;
    PangoRectangle logical_rect;
    PangoLayout *layout;
    PangoFontMetrics *metrics;
    
    gdk_color_parse("black", &AR.FgPixel);
    gdk_color_parse("white", &AR.BgPixel);

    desc = pango_font_description_from_string("fixed 11");
    context = gdk_pango_context_get();
    pango_context_set_font_description(context,desc);
    pango_context_set_language(context, pango_language_from_string("UNKNOWN"));

    AR.LabelFont = pango_context_load_font(context,desc);
    metrics = pango_font_get_metrics(AR.LabelFont, 
                                     pango_context_get_language(context));
    layout = pango_layout_new(context);

    UI->FgPixel = AR.FgPixel;
    UI->BgPixel = AR.BgPixel;

    /* Calculate the meter label dimensions */
    PM->LabelWp = PM->LabelHp = 0;
    UI->LabelFont = AR.LabelFont;
 
    if (UI->LabelFont == NULL) {
        gchar *fname = "-misc-fixed-medium-r-normal--10-*-*-*-*-*-iso8859-1";
        desc = pango_font_description_from_string(fname);

        if ((UI->LabelFont = pango_context_load_font(context, desc)) == NULL) {
            desc = pango_font_description_from_string("fixed 11");
            if ((UI->LabelFont = pango_context_load_font(context, desc)) == NULL) {
                PmError(PmERR_LABELFONT, NULL);
                exit(1);
            }
        }
    }
 
    for (i = 0; i < PmMAX_METERS; i++) {
        pango_layout_set_text(layout, PM->Meters[i].TRName, -1);
        pango_layout_get_pixel_extents(layout, NULL, &logical_rect);
        ascent =  PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
        descent= PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
        pango_layout_get_pixel_extents(layout, NULL, &logical_rect);  

        PmAssignMax(PM->LabelWp, (int) logical_rect.width);
        PmAssignMax(PM->LabelHp, (int) (ascent + descent));
    }
 
    pango_layout_set_text(layout, _("00000"), -1);
    pango_layout_get_pixel_extents(layout, NULL, &logical_rect);
    ascent =  PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));;
    descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));                 
    pango_layout_get_pixel_extents(layout, NULL, &logical_rect);  

    PM->ValueWp = (int) logical_rect.width;
    PmAssignMax(PM->LabelHp, (int) (ascent + descent));

    g_object_unref(G_OBJECT(context));
    g_object_unref(G_OBJECT(layout));
    pango_font_metrics_unref(metrics);
}


/* Initialize resources.
 *
 * Load the application resource converters and pre-initialize the 
 * meter data structures.
 */

static void 
InitResources()
{
    int i;
    GdkColor blue, red, grey, black;

    gdk_color_parse("blue", &blue);
    gdk_color_parse("red", &red);
    gdk_color_parse("grey", &grey);
    gdk_color_parse("black", &black);

    /* Initialize the meter structures */
    for (i = 0; i < PmMAX_METERS; i++) {
        PM->Meters[i].MinMax  = 0;
        PM->Meters[i].MaxMax  = PmMAX_VALUE;
        PM->Meters[i].CurMax  = PmMAX_VALUE;
        PM->Meters[i].Limit   = PmMAX_VALUE;
        PM->Meters[i].Scale   = 1;
        PM->Meters[i].RangeInc = 100;
    }

    /* cpu */
    PM->Meters[PM_CPU].Type = PM_CPU;
    PM->Meters[PM_CPU].Name = N_("cpu");
    PM->Meters[PM_CPU].TRName = _("cpu");
    PM->Meters[PM_CPU].RName = "cpu";
    PM->Meters[PM_CPU].MinMax = 100;
    PM->Meters[PM_CPU].MaxMax = 100;
    PM->Meters[PM_CPU].CurMax = 100;
    PM->Meters[PM_CPU].Limit = 50;
    PM->Meters[PM_CPU].RangeInc = 1;
    PM->Meters[PM_CPU].MeterColor = blue;
    PM->Meters[PM_CPU].LimitColor = red;
    
    /* pkts */
    PM->Meters[PM_PKTS].Type = PM_PKTS;
    PM->Meters[PM_PKTS].Name = N_("packets");
    PM->Meters[PM_PKTS].TRName = _("packets");
    PM->Meters[PM_PKTS].RName = "packets";
    PM->Meters[PM_PKTS].MinMax = 32;
    PM->Meters[PM_PKTS].MaxMax = PmMAX_VALUE;
    PM->Meters[PM_PKTS].CurMax = 32;
    PM->Meters[PM_PKTS].Limit = 16;
    PM->Meters[PM_PKTS].MeterColor = blue;
    PM->Meters[PM_PKTS].LimitColor = red;

    /* page */
    PM->Meters[PM_PAGE].Type = PM_PAGE;
    PM->Meters[PM_PAGE].Name = N_("page");
    PM->Meters[PM_PAGE].TRName = _("page");
    PM->Meters[PM_PAGE].RName = "page";
    PM->Meters[PM_PAGE].MinMax = 16;
    PM->Meters[PM_PAGE].MaxMax = PmMAX_VALUE;
    PM->Meters[PM_PAGE].CurMax = 16;
    PM->Meters[PM_PAGE].Limit = 8;
    PM->Meters[PM_PAGE].MeterColor = blue;
    PM->Meters[PM_PAGE].LimitColor = red;

    /* swap */
    PM->Meters[PM_SWAP].Type = PM_SWAP;
    PM->Meters[PM_SWAP].Name = N_("swap");
    PM->Meters[PM_SWAP].TRName = _("swap");
    PM->Meters[PM_SWAP].RName = "swap";
    PM->Meters[PM_SWAP].MinMax = 4;
    PM->Meters[PM_SWAP].MaxMax = PmMAX_VALUE;
    PM->Meters[PM_SWAP].CurMax = 4;
    PM->Meters[PM_SWAP].Limit = 2;
    PM->Meters[PM_SWAP].MeterColor = blue;
    PM->Meters[PM_SWAP].LimitColor = red;

    /* intr */
    PM->Meters[PM_INTR].Type = PM_INTR;
    PM->Meters[PM_INTR].Name = N_("interrupts");
    PM->Meters[PM_INTR].TRName = _("interrupts");
    PM->Meters[PM_INTR].RName = "interrupts";
    PM->Meters[PM_INTR].MinMax = 100;
    PM->Meters[PM_INTR].MaxMax = PmMAX_VALUE;
    PM->Meters[PM_INTR].CurMax = 100;
    PM->Meters[PM_INTR].Limit = 50;
    PM->Meters[PM_INTR].MeterColor = blue;
    PM->Meters[PM_INTR].LimitColor = red;

    /* disk */
    PM->Meters[PM_DISK].Type = PM_DISK;
    PM->Meters[PM_DISK].Name = N_("disk");
    PM->Meters[PM_DISK].TRName = _("disk");
    PM->Meters[PM_DISK].RName = "disk";
    PM->Meters[PM_DISK].MinMax = 40;
    PM->Meters[PM_DISK].MaxMax = PmMAX_VALUE;
    PM->Meters[PM_DISK].CurMax = 40;
    PM->Meters[PM_DISK].Limit = 20;
    PM->Meters[PM_DISK].MeterColor = blue;
    PM->Meters[PM_DISK].LimitColor = red;

    /* cntx */
    PM->Meters[PM_CNTX].Type = PM_CNTX;
    PM->Meters[PM_CNTX].Name = N_("context");
    PM->Meters[PM_CNTX].TRName = _("context");
    PM->Meters[PM_CNTX].RName = "context";
    PM->Meters[PM_CNTX].MinMax = 64;
    PM->Meters[PM_CNTX].MaxMax = PmMAX_VALUE;
    PM->Meters[PM_CNTX].CurMax = 64;
    PM->Meters[PM_CNTX].Limit = 32;
    PM->Meters[PM_CNTX].MeterColor = blue;
    PM->Meters[PM_CNTX].LimitColor = red;

    /* load */
    PM->Meters[PM_LOAD].Type = PM_LOAD;
    PM->Meters[PM_LOAD].Name = N_("load");
    PM->Meters[PM_LOAD].TRName = _("load");
    PM->Meters[PM_LOAD].RName = "load";
    PM->Meters[PM_LOAD].MinMax = 4;
    PM->Meters[PM_LOAD].MaxMax = PmMAX_VALUE;
    PM->Meters[PM_LOAD].CurMax = 4;
    PM->Meters[PM_LOAD].Limit = 2;
    PM->Meters[PM_LOAD].Scale = FSCALE;
    PM->Meters[PM_LOAD].MeterColor = blue;
    PM->Meters[PM_LOAD].LimitColor = red;

    /* coll */
    PM->Meters[PM_COLL].Type = PM_COLL;
    PM->Meters[PM_COLL].Name = N_("collisions");
    PM->Meters[PM_COLL].TRName = _("collisions");
    PM->Meters[PM_COLL].RName = "collisions";
    PM->Meters[PM_COLL].MinMax = 4;
    PM->Meters[PM_COLL].MaxMax = PmMAX_VALUE;
    PM->Meters[PM_COLL].CurMax = 4;
    PM->Meters[PM_COLL].Limit = 2;
    PM->Meters[PM_COLL].Scale = FSCALE;
    PM->Meters[PM_COLL].MeterColor = blue;
    PM->Meters[PM_COLL].LimitColor = red;

    /* errs */
    PM->Meters[PM_ERRS].Type = PM_ERRS;
    PM->Meters[PM_ERRS].Name = N_("errors");
    PM->Meters[PM_ERRS].TRName = _("errors");
    PM->Meters[PM_ERRS].RName = "errors";
    PM->Meters[PM_ERRS].MinMax = 4;
    PM->Meters[PM_ERRS].MaxMax = PmMAX_VALUE;
    PM->Meters[PM_ERRS].CurMax = 4;
    PM->Meters[PM_ERRS].Limit = 2;
    PM->Meters[PM_ERRS].Scale = FSCALE;
    PM->Meters[PM_ERRS].MeterColor = blue;
    PM->Meters[PM_ERRS].LimitColor = red;


    PM->SampleInterval = 1;
    PM->SampleModifier = 1;
    PM->OldSocket  = -1;
    PM->RpcVersion = PmRPC_VERSION_NONE;
    PM->Orientation = PM_VERTICAL;
    PM->WrapCount   = 10;
    PM->Resizeable  = TRUE;
    PM->LogLineCount  = 0;
    PM->LogPageLength = PmPAGE_LENGTH;
    PM->AutoLoadByHost = FALSE;
    PM->SaveByHost = TRUE;
    PM->LoadByHost = TRUE;

    /* FIXME: The following setting has no effect as its been over rided 
     * in CreateInterface of perfmeter.c
     */
    PM->CanvasColor =  grey; 
    PM->LabelColor = black; 
    PM->ShowHostName = FALSE;
    PM->ShowMenubar = TRUE;
    PM->ShowStripChart = TRUE;
    PM->ShowLiveActionBar = TRUE;
    PM->inFrontPanel = FALSE;

    PMSETDISPLAYLIST(NULL);
    PmSetGraphType(PM_LINE);
}


/* Check meter limits to be sure values are between the min and maximum. */

static void 
PmCheckLimits()
{
    int i;

    for (i = 0; i < PmMAX_METERS; i++) {
        PmRangeLT(PM->Meters[i].MinMax, 0, 0);
        PmRangeGT(PM->Meters[i].MaxMax, PmMAX_VALUE, PmMAX_VALUE);
        PmRangeLT(PM->Meters[i].MaxMax, PM->Meters[i].MinMax, PmMAX_VALUE);
        PmRangeGT(PM->Meters[i].MinMax, 0.8 * PM->Meters[i].MaxMax,
                  0.8 * PM->Meters[i].MaxMax);
        PmRangeLT(PM->Meters[i].CurMax, PM->Meters[i].MinMax,
                  PM->Meters[i].MinMax);
        PmRangeGT(PM->Meters[i].CurMax, PM->Meters[i].MaxMax,
                  PM->Meters[i].MaxMax);
        PmRangeLT(PM->Meters[i].Limit, 0,PM->Meters[i].MinMax);
        PmRangeGT(PM->Meters[i].Limit, PM->Meters[i].MaxMax,
                  PM->Meters[i].MaxMax);
    }
}


/*ARGSUSED*/
static gboolean
pm_arguments_post_parse(GOptionContext *context, GOptionGroup *group,
                        gpointer data, GError **error)
{
    gboolean meter_init = FALSE;
    int DL[PmMAX_METERS];
    int i, meter;
    long value;
    gchar *charset;
    gchar **argv, *s;
    char *err = NULL;

    if (opt_all) {
        for (i = 0; i < PmMAX_METERS;i++) {
            DL[i] = i;
        }

        PMSETDISPLAYLIST(DL);
    }

    if (opt_panel) {
        PM->inFrontPanel = TRUE;
        PM->ShowStripChart = FALSE;
        PM->ShowLiveActionBar = TRUE;
        PM->IsRemote = FALSE;
    }

    if (opt_log) {
        PM->Log = TRUE;
    }

    if (opt_logfile != NULL) {
        PmStrcpy(PM->LogFilename, opt_logfile);
        PM->Log = TRUE;
    }

    if (opt_pagelength != 0) {
        PmAssignMin(opt_pagelength, PmMAX_VALUE);
        PmAssignMax(opt_pagelength, 0);
        PM->LogPageLength = opt_pagelength;
        if (PM->LogPageLength < 1) {
            PM->LogPageLength = PmPAGE_LENGTH;
        }

        PM->LogLineCount = PM->LogPageLength;
    }

    if (opt_sampletime != 0) {
        PmAssignMin(opt_sampletime, PmMAX_SAMPLETIME);
        PmAssignMax(opt_sampletime, 0);
        PM->SampleInterval = opt_sampletime;
        PM->SampleModifier = 1;
    }

    if (opt_meter != NULL) {
        /* Check for each meter name */
        for (i = 0, meter = -1; i < PmMAX_METERS; i++) {
            if (g_strcasecmp(PM->Meters[i].RName, opt_meter) == 0) {
                meter = i;
                break;
            } else if (g_strcasecmp("colls", opt_meter) == 0) {
                /* Check for colls separately; necessary for backwards
                 * compatability.
                 */

                /* Get the meter number for coll */
                for (i = 0, meter =- 1; i < PmMAX_METERS; i++) {
                    if (g_strcasecmp(PM->Meters[i].Name, "coll") == 0) {
                        meter = i;
                        break;
                    }
                }
            }
        }
 
        /* User passed invalid meter name */
        if (meter < 0) {
            Usage();
        }

        if (!meter_init) {
            /* Clear all the meters */
            PmLock();
            meter_init = TRUE;
            PM->NMeters = 0;
            for (i = 0; i < PmMAX_METERS; i++) {
                PM->DisplayList[i] = -1;
                PM->Meters[i].Redisplay = FALSE;
                PM->Meters[i].Showing = FALSE;
            }
        }
        PmResetMeter(meter, TRUE);
        PmUnLock();
    }

    if (opt_ceiling) {
        argv = g_strsplit(opt_ceiling, " ", 10);

        /* Check if any of the token is NULL */
        if ((argv[0] == NULL) || (argv[1] == NULL)) {
            Usage();
        } 

        for (i = 0, meter =- 1; i < PmMAX_METERS; i++) {
            if (g_strcasecmp(PM->Meters[i].Name, argv[0]) == 0) {
                meter = i;
                break;
            }
        }    
        if (meter >= 0) {
            value = strtol(argv[1], NULL, 10);
            PmAssignMin(value, PmMAX_VALUE);
            PmAssignMax(value, 0);
            PM->Meters[meter].Limit = (int) value;
        } else {
            Usage();
        }

        if (!meter_init) {
            /* Clear all the meters */
            PmLock();
            meter_init = TRUE;
            PM->NMeters = 0;
            for (i = 0; i < PmMAX_METERS; i++) {
                PM->DisplayList[i] = -1;
                PM->Meters[i].Redisplay = FALSE;
                PM->Meters[i].Showing = FALSE;
            }
        }
        PmResetMeter(meter, TRUE);
        PmUnLock();

        g_strfreev(argv);
    }

    PM->Debug = opt_debug;

    if (opt_horizontal) {
        PM->Orientation = PM_HORIZONTAL;
    }

    if (opt_line) {
        PmSetGraphType(PM_LINE);
    }

    if (opt_solid) {
        PmSetGraphType(PM_SOLID);
    }

    if (opt_version) {
        s = _("%s: version %s\n");
        g_print(g_locale_from_utf8(s, -1, NULL, NULL, NULL), PM->AppClass,
                VERSION);
        exit(0);
    }

    if (opt_vertical) {
        PM->Orientation = PM_VERTICAL;
    }

    if (opt_maxvalue) {
        if (opt_maxvalue == NULL) {
            Usage();
        }

        argv = g_strsplit(opt_maxvalue, " ", 10);

        /* Check if any of the token is NULL */
        if ((argv[0] == NULL) || (argv[1] == NULL) ||
            (argv[2] == NULL) || (argv[3] == NULL)) {
            Usage();
        }  

        for (i = 0, meter =- 1; i < PmMAX_METERS; i++) {
            if (g_strcasecmp(PM->Meters[i].Name, argv[0]) == 0) {
                meter = i;
                break;
            }
        }
 
        if (meter < 0) {
            Usage();
        }

        value = strtol(argv[1], &err, 10);
        if (*err || value < 0) {
            Usage();
        }

        PmAssignMin(value, PmMAX_VALUE);
        PmAssignMax(value, 0);
        PM->Meters[meter].CurMax = (int) value;

        value = strtol(argv[2], &err, 10);
        if (*err || value < 0) {
            Usage();
        }

        PmAssignMin(value, PmMAX_VALUE);
        PmAssignMax(value, 0);
        PM->Meters[meter].MinMax = (int) value;

        value = strtol(argv[3], &err, 10);
        if (*err || value < 0) {
            Usage();
        }

        PmAssignMin(value, PmMAX_VALUE);
        PmAssignMax(value, 0);
        PM->Meters[meter].MaxMax = (int) value;

        if (!meter_init) {

            /* Clear all the meters */
            PmLock();
            meter_init = TRUE;
            PM->NMeters = 0;
            for (i = 0; i < PmMAX_METERS; i++) {
                PM->DisplayList[i] = -1;
                PM->Meters[i].Redisplay = FALSE;
                PM->Meters[i].Showing = FALSE;
            }
        }
        PmResetMeter(meter, TRUE);
        PmUnLock();

        g_strfreev(argv);
    }

    if (opt_hostnames && opt_hostnames[0] != NULL) {
        PM->Hostname = g_strdup(opt_hostnames[0]);

        if (g_strcasecmp(PM->Hostname, PM->LocalMachine) == 0) {
            PM->IsRemote = FALSE;
        } else {
            PM->IsRemote = TRUE;
        }
    }

    return(FALSE);
}


/* Scan the command line options and override the application data. */

static void
ParseCmdOptions(int argc, char **argv)
{
    int i;
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
    const char *charset;
#endif /*HAVE_BIND_TEXTDOMAIN_CODESET*/

    const char **args;
    gboolean retval, host = FALSE;
    GError *error = NULL;
    GnomeClient *client;    
    GOptionGroup *main_group;

#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
    g_get_charset(&charset);
    BIND_TEXTDOMAIN_CODESET(GETTEXT_PACKAGE, charset);
#endif /*HAVE_BIND_TEXTDOMAIN_CODESET*/

    gnome_program_init(PM->AppName, VERSION,
                       LIBGNOMEUI_MODULE,
                       argc, argv,
                       GNOME_PARAM_APP_DATADIR, DATADIR, NULL);    

    /* Note if any options were passed */
    if (argc >= 2) {
        if (strlen(argv[1]) >=2 && argv[1][0] == '-') {
            cmd_line = TRUE;
        }
    }

#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
    BIND_TEXTDOMAIN_CODESET(GETTEXT_PACKAGE, "UTF-8");
#endif /*HAVE_BIND_TEXTDOMAIN_CODESET*/

    ctx = g_option_context_new("- monitor performance ");

#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
    g_option_context_add_main_entries(ctx, entries, GETTEXT_PACKAGE);
#else
    g_option_context_add_main_entries(ctx, entries, NULL);
#endif

    /* Enable automatic generation of --help output */
    g_option_context_set_help_enabled(ctx, TRUE);
    g_option_context_set_ignore_unknown_options(ctx, FALSE);

    /* Set pre and post parse hooks */
    main_group = g_option_context_get_main_group(ctx);
    g_option_group_set_parse_hooks(main_group, NULL, pm_arguments_post_parse);

    retval = g_option_context_parse(ctx, &argc, &argv, &error);
    g_assert(retval == FALSE);

    client = gnome_master_client();
    g_signal_connect(GTK_OBJECT(client), "save_yourself",
                     G_CALLBACK(save_session), (gpointer) argv[0]);
    g_signal_connect(GTK_OBJECT(client), "die",
                     G_CALLBACK(client_die), NULL);

    /* Don't show menubar when in front panel */
    if (PM->inFrontPanel == TRUE) {
        PM->ShowMenubar = FALSE;
        PM->ShowStripChart = FALSE;
        PM->ShowLimit = FALSE;
    }

    g_option_context_free(ctx);
}


GConfValue *
GetGConfResource(char *host, char *meter, char *key)
{
    gchar *key_buf;
    GConfValue *result;

    if (host && meter) {
        key_buf = g_strdup_printf("/apps/gperfmeter/%s/%s/%s", 
                                  host, meter, key);
    } else if (host) {
        key_buf = g_strdup_printf("/apps/gperfmeter/%s/%s", host, key);
    } else if (meter) {
        key_buf = g_strdup_printf("/apps/gperfmeter/%s/%s", meter, key);
    } else {
        key_buf = g_strdup_printf("/apps/gperfmeter/%s", key);
    }

    result = gconf_client_get(PM->gconf_client, key_buf, NULL);

    g_free(key_buf);

    return(result);
}


void
PutResource(char *host, char *meter, char *key, 
            ResType key_value_type, gpointer key_value)
{
    gchar *key_buf;
    GError *error = NULL;

    /* Key formation */
    if (host && meter) {
        key_buf = g_strdup_printf("/apps/gperfmeter/%s/%s/%s", 
                                  host, meter, key);
    } else if (host) {
        key_buf = g_strdup_printf("/apps/gperfmeter/%s/%s", host, key);
    } else if (meter) {
        key_buf = g_strdup_printf("/apps/gperfmeter/%s/%s", meter, key);
    } else {
        key_buf = g_strdup_printf("/apps/gperfmeter/%s", key);
    }

    /* Setting the key value. */
    switch (key_value_type) {
        case RES_STRING:
            gconf_client_set_string(PM->gconf_client, key_buf,
                                    (gchar *) key_value, &error);
            break;

        case RES_INT:
            gconf_client_set_int(PM->gconf_client, key_buf,
                                 (gint) key_value, &error);
            break;

        case RES_BOOL:
            gconf_client_set_bool(PM->gconf_client, key_buf,
                                  (gboolean) key_value, &error);
            break;

        default:
            g_warning("PutResource : wrong GConfValueType\n");
            return;
    }

    if (error) {
        g_warning("Error in %s : %s", g_quark_to_string(error->domain),
                  error->message);
    }

    g_free(key_buf);

    return;
}


/* Write the application resource to file. */

void 
PmSaveResources()
{
    int i;
    char *hname, *mname;
    gchar *temp_buf = NULL, *tmp_num = NULL;
    GString *tmp;

    /* General Resources */

    hname = mname = 0;

    PutResource(hname, mname, "CDEmigration", RES_BOOL,
                GINT_TO_POINTER(TRUE));

    PutResource(hname, mname, "CollectWhenIconized", RES_BOOL, 
                GINT_TO_POINTER(PM->CollectWhenIconized));
    PutResource(hname, mname, "SaveByHost", RES_BOOL, 
                GINT_TO_POINTER(PM->SaveByHost));
    PutResource(hname, mname, "AutoLoadByHost", RES_BOOL,
                GINT_TO_POINTER(PM->AutoLoadByHost));
    PutResource(hname, mname, "LoadByHost", RES_BOOL, 
                GINT_TO_POINTER(PM->LoadByHost));
    PutResource(hname, mname, "IsRemote", RES_BOOL,
                GINT_TO_POINTER(PM->IsRemote));
    PutResource(hname, mname, "HostName", RES_STRING, PM->Hostname);

    for (i = 0; i < PmMAX_HOST_LIST_SIZE; i++) {
        GList *item;

        temp_buf = g_strdup_printf("HostName%1d", i);
        if ((item = g_list_nth(remote_hosts, i)) != NULL) {
            PutResource(hname, mname, temp_buf, RES_STRING, item->data);
        }
        g_free(temp_buf);
    }

    PutResource(hname, mname, "ShowLimit", RES_BOOL, 
                GINT_TO_POINTER(PM->ShowLimit));
    PutResource(hname, mname, "Log", RES_BOOL, GINT_TO_POINTER(PM->Log));
    PutResource(hname, mname, "LogFilename", RES_STRING, PM->LogFilename);
    PutResource(hname, mname, "LogPageLength", RES_INT,
                GINT_TO_POINTER(PM->LogPageLength));

    /* Host general resources */

    if (PM->SaveByHost) {
        if (PM->IsRemote) {
            hname = PM->Hostname;
        } else {
            hname = PM->LocalMachine;
        }
    }
  
    PutResource(hname, mname, "SampleInterval", RES_INT,
                GINT_TO_POINTER(PM->SampleInterval));
    PutResource(hname, mname, "SampleModifier", RES_INT, 
                GINT_TO_POINTER(PM->SampleModifier));
    PutResource(hname, mname, "WrapCount", RES_INT, 
                GINT_TO_POINTER(PM->WrapCount));
    PutResource(hname, mname, "SizeToFit", RES_BOOL, 
                GINT_TO_POINTER(PM->Resizeable));
    PutResource(hname, mname, "GraphType", RES_STRING, 
                (PM->GraphType == PM_LINE ? "line" : "solid"));
    
    PutResource(hname, mname, "Direction", RES_STRING,
                (PM->Orientation == PM_VERTICAL ? "vertical" : "horizontal"));
    
    PutResource(hname, mname, "ShowHostName", RES_BOOL, 
                GINT_TO_POINTER(PM->ShowHostName));
    PutResource(hname, mname, "ShowMenubar", RES_BOOL, 
                GINT_TO_POINTER(PM->ShowMenubar));
    PutResource(hname, mname, "ShowStripChart", RES_BOOL, 
                GINT_TO_POINTER(PM->ShowStripChart));
    PutResource(hname, mname, "ShowLiveActionBar", RES_BOOL, 
                GINT_TO_POINTER(PM->ShowLiveActionBar));

    tmp = g_string_new ("");
    for (i = 0; i < PM->NMeters; i++) {
        if (i > 0) {
            tmp = g_string_append(tmp, ",");
        }

        tmp_num = g_strdup_printf("%d", PM->DisplayList[i]);
        tmp = g_string_append(tmp, tmp_num);
        g_free(tmp_num);
    }
    PutResource(hname, mname, "DisplayList", RES_STRING, tmp->str);
    g_string_free(tmp, TRUE);

    /* Host data resources */

    /* Meter values */
    for (i = 0; i < PmMAX_METERS; i++) {
        mname = PM->Meters[i].RName;
        PutResource(hname, mname, "CurMax", RES_INT, 
                    GINT_TO_POINTER(PM->Meters[i].CurMax));
        PutResource(hname, mname, "Limit", RES_INT, 
                    GINT_TO_POINTER(PM->Meters[i].Limit));
        PutResource(hname, mname, "MaxMax", RES_INT, 
                    GINT_TO_POINTER(PM->Meters[i].MaxMax));
        PutResource(hname, mname, "MinMax", RES_INT, 
                    GINT_TO_POINTER(PM->Meters[i].MinMax));

        temp_buf = color_to_string(&PM->Meters[i].MeterColor);
        if (temp_buf) {
            PutResource(hname, mname, "MeterColor", RES_STRING, temp_buf);
            g_free(temp_buf);
        }    
     
        temp_buf = color_to_string(&PM->Meters[i].LimitColor);
        if (temp_buf) {
            PutResource(hname, mname, "LimitColor", RES_STRING, temp_buf);
            g_free(temp_buf);
        }    
    }

#ifdef DEBUG
    if (hname != NULL && mname != NULL) {
        g_print("Save resource to: %s %s\n", hname, mname);
    }
#endif
}


/* Reset the attributes from the latest saved ones. */

/*ARGSUSED*/
gboolean 
PmResetResources(char *filename)
{
    GConfValue *gptr;
    char buf[PmMAX_LINE], *hname, *mname;
    int i;
    gchar *temp_buf;
    GdkColor color;

    PmLock();

#ifdef DEBUG
    if (filename != NULL) {
        g_print("Reset attributes from: %s\n", filename);
    }
#endif

    hname = mname = 0;
    
    if ((gptr = GetGConfResource(hname, mname, "SaveByHost"))) {
        PM->SaveByHost = gconf_value_get_bool(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "AutoLoadByHost"))) {
        PM->AutoLoadByHost = gconf_value_get_bool(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "LoadByHost"))) {
        PM->LoadByHost = gconf_value_get_bool(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "ShowLimit"))) {
        PM->ShowLimit = gconf_value_get_bool(gptr);
    }
    
    if ((gptr = GetGConfResource(hname, mname, "IsRemote"))) {
        PM->IsRemote = gconf_value_get_bool(gptr); 
    }

    g_list_free(remote_hosts);
    remote_hosts = NULL;
    for (i = 0; i < PmMAX_HOST_LIST_SIZE; i++) {
        gchar *resname = g_strdup_printf("HostName%1d", i);

        if ((gptr = GetGConfResource(hname, mname, resname))) {
            const char *data = gconf_value_get_string(gptr);

            if (g_list_find_custom(remote_hosts, data,
                                   (GCompareFunc) g_ascii_strcasecmp) == NULL) {
                remote_hosts = g_list_insert_sorted(remote_hosts,
                                                    (gpointer) data,
                                             (GCompareFunc) g_ascii_strcasecmp);
            }
        }
        g_free(resname);
    }

    if ((gptr = GetGConfResource(hname, mname, "HostName"))) {
        PM->Hostname = g_strdup(gconf_value_get_string(gptr));
    }

    if (PM->LoadByHost) {
        if (PM->IsRemote) {
            hname = PM->Hostname;
        } else {
            hname = PM->LocalMachine;
        }
    }

    if ((gptr = GetGConfResource(hname, mname, "CollectWhenIconized"))) {
        PM->CollectWhenIconized = gconf_value_get_bool(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "Log"))) {
        PM->Log = gconf_value_get_bool(gptr);
    }
    
    if ((gptr = GetGConfResource(hname, mname, "LogFilename"))) {
        PM->LogFilename = g_strdup(gconf_value_get_string (gptr));
    }

    if ((gptr = GetGConfResource(hname, mname, "LogPageLength"))) {
        PM->LogPageLength = gconf_value_get_int(gptr);
    }
    
    if ((gptr = GetGConfResource(hname, mname, "SampleInterval"))) {
        PM->SampleInterval = gconf_value_get_int(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "SampleModifier"))) {
        PM->SampleModifier = gconf_value_get_int(gptr);
    }
    
    if ((gptr = GetGConfResource(hname, mname, "WrapCount"))) {
        PM->WrapCount = gconf_value_get_int(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "SizeToFit"))) {
        PM->Resizeable = gconf_value_get_bool(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "GraphType"))) {
        const char * graph_type = gconf_value_get_string(gptr);

        if (g_strcasecmp(graph_type, "line") == 0) {
            PM->GraphType = PM_LINE;
        } else if (g_strcasecmp(graph_type, "solid") == 0) {
            PM->GraphType = PM_SOLID;
        }
    }
    if ((gptr = GetGConfResource(hname, mname, "Direction"))) {
        const char * direction = gconf_value_get_string(gptr);

        if (g_strcasecmp(direction, "vertical") == 0) {
            PM->Orientation = PM_VERTICAL;
        } else if (g_strcasecmp(direction, "horizontal") == 0) {
            PM->Orientation = PM_HORIZONTAL;
        }
    }

    if ((gptr = GetGConfResource(hname, mname, "ShowHostName"))) {
        PM->ShowHostName = gconf_value_get_bool(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "ShowMenubar"))) {
        PM->ShowMenubar = gconf_value_get_bool(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "ShowStripChart"))) {
        PM->ShowStripChart = gconf_value_get_bool(gptr);
    }

    if ((gptr = GetGConfResource(hname, mname, "ShowLiveActionBar"))) {
        PM->ShowLiveActionBar = gconf_value_get_bool(gptr);
    }
    
    if ((gptr = GetGConfResource(hname, mname, "DisplayList"))) {
        char *s;
        gchar *dl;
        int display_list[PmMAX_METERS];

        for (i = 0; i < PmMAX_METERS; i++) {
            display_list[i] = -1;
        }

        PmStrcpy(buf, gconf_value_get_string(gptr));
        s = strtok(buf, " ,:");
        for (i = 0; i < PmMAX_METERS && s; i++) {
            gdouble value = g_strtod(s, NULL);

            if ((value >= 0) && (value < PmMAX_METERS)) {
                display_list[i] = value;
            }
            s = strtok(NULL, " ,:");
        }

        PMSETDISPLAYLIST(display_list);
    }

    /* Meter data resources */
    
    for (i = 0; i < PmMAX_METERS; i++) {
        mname = PM->Meters[i].Name;

        if ((gptr = GetGConfResource(hname, mname, "CurMax"))) {
            PM->Meters[i].CurMax = gconf_value_get_int(gptr);
        }

        if ((gptr = GetGConfResource(hname, mname, "Limit"))) {
            PM->Meters[i].Limit = gconf_value_get_int(gptr);
        }

        if ((gptr = GetGConfResource(hname, mname, "MaxMax"))) {
            PM->Meters[i].MaxMax = gconf_value_get_int(gptr);
        }

        if ((gptr = GetGConfResource(hname, mname, "MinMax"))) {
            PM->Meters[i].MinMax = gconf_value_get_int(gptr);
        }

        if ((gptr = GetGConfResource(hname, mname, "MeterColor"))) {
            if (gdk_color_parse(gconf_value_get_string(gptr), &color)) {
               PM->Meters[i].MeterColor = color;  
            }
        }
        if ((gptr = GetGConfResource(hname, mname, "LimitColor"))) {
           if (gdk_color_parse(gconf_value_get_string(gptr), &color)) {
               PM->Meters[i].LimitColor = color;
           }           
        }
    }

    PmCheckLimits();
    PmSetScaleFactors();
    PM->LogFilename = expand_tilde(PM->LogFilename);
    PmUnLock();
    PmResetProps();

    return(TRUE);
}


/* Write the command wm property. */

extern void 
PmSaveCommand()
{
    /* Initial command line */
    if (UI->MainWin) {
        XSetCommand(GDK_DISPLAY(), GDK_WINDOW_XWINDOW(UI->MainWin->window),
                    UI->Argv, UI->Argc);
    }
}


static void 
Usage()
{
    char *charset, *usage;

/*
 *    Prints gperfmeter --help message, followed by:
 *
 *        meter is one of:
 *            cpu, pkts, page, swap, intr, disk, cntxt, load, colls, errs
 *
 *    Keyboard accelerators:
 *        a        - show all meters
 *        Ctrl+1   - show CPU
 *        Ctrl+2   - show load
 *        Ctrl+3   - show disk
 *        Ctrl+4   - show page
 *        Ctrl+5   - show cntx
 *        Ctrl+6   - show swap
 *        Ctrl+7   - show intr
 *        Ctrl+8   - show pkts
 *        Ctrl+9   - show coll
 *        Ctrl+0   - show errs
 *        Ctrl+M   - toggle menubar
 *        n        - toggle monitoring mode (local/remote)
 *        s        - toggle graph style
 *        ^        - toggle showing the limit line
 *        t        - toggle writing to the log file
 *        y        - toggle orientation (horizontal/vertical)
 *        r        - toggle showing strip chart
 *        p        - show properties
 *        Ctrl+Q   - quit perfmeter
 *        1-9      - set sampletime to a range from 1-9 seconds
 *        +        - increment the sample time by 1 second
 *        -        - decrement the sample time by 1 second
 */

    usage = _("\n\tmeter is one of:\n\
             cpu, packets, page, swap, interrupts, disk, context, load, collisions, errors\n\n\
             Keyboard accelerators:\n\n\
             a      - show all meters\n\
             Ctrl+1 - show CPU\n\
             Ctrl+2 - show load\n\
             Ctrl+3 - show disk\n\
             Ctrl+4 - show page\n\
             Ctrl+5 - show context\n\
             Ctrl+6 - show swap\n\
             Ctrl+7 - show interrupts\n\
             Ctrl+8 - show packets\n\
             Ctrl+9 - show collisions\n\
             Ctrl+0 - show errors\n\
             Ctrl+M - toggle menubar\n\
             Ctrl+Q - quit perfmeter\n\
             n      - toggle monitoring mode (local/remote)\n\
             s      - toggle graph style\n\
             ^      - toggle showing the limit line\n\
             t      - toggle writing to the log file\n\
             y      - toggle orientation (horizontal/vertical)\n\
             r      - toggle showing strip chart\n\
             p      - show properties\n\
             1-9    - set sampletime to a range from 1-9 seconds\n\
             +      - increment the sample time by 1 second\n\
             -      - decrement the sample time by 1 second\n");

    charset = g_locale_from_utf8(usage, -1, NULL, NULL, NULL);
    g_printerr (charset);
    g_free(charset);

    exit(1);
}


/*
 * Function
 *     expand_tilde
 *
 * Description
 *     Takes a user-typed filename and expands a tilde at the beginning of
 *     the string
 *
 *     This routine was stolen from GNOME CVS (beagle)
 *       
 * Parameters
 *     filename    unexpanded path
 *
 * Returns
 *     expanded path
 *
 */

gchar * 
expand_tilde(const char *filename)
{
    const char *home, *notilde, *slash;

    if (!filename) {
        return(NULL);
    }

    if (filename[0] != '~') {
        return(g_strdup(filename));
    }

    notilde = filename + 1;

    slash = strchr(notilde, G_DIR_SEPARATOR);
    if (!slash) {
        return(NULL);
    }

    if (slash == notilde) {
        home = g_get_home_dir();

        if (!home) {
            return(g_strdup(filename));
        }
    } else {
        char *username;
        struct passwd *passwd;

        username = g_strndup(notilde, slash - notilde);
        passwd = getpwnam(username);
        g_free(username);

        if (!passwd) {
            return(g_strdup(filename));
        }

        home = passwd->pw_dir;
    }

    return(g_build_filename(home, G_DIR_SEPARATOR_S, slash + 1, NULL));
}


/*ARGSUSED*/
static int 
save_session(GnomeClient *client, gint phase, GnomeRestartStyle save_style,
             gint shutdown, GnomeInteractStyle interact_style,
             gint fast, gpointer client_data)
{
    gchar *argv[] = { "rm", "-r", NULL };

    argv[0] = (char *) client_data;
    gnome_client_set_clone_command(client, 1, argv);
    gnome_client_set_restart_command(client, 1, argv);

    return(TRUE);
}


/*ARGSUSED*/
static gint 
client_die(GnomeClient *client, gpointer client_data)
{
    gtk_main_quit(); 

    return(FALSE);
}


static char *
color_to_string(const GdkColor *color)
{
    char *s = g_strdup_printf("#%02X%02X%02X",
                              color->red / 256,
                              color->green / 256,
                              color->blue / 256);

    return(s);
}

/* Generate a name for a temporary file */
gchar *
pm_log_create_tmp_filename(void)
{
    gchar *filename, *tmp;

    tmp = g_strdup_printf("%s.log.%ld", PM->AppName, getpid());
    filename = g_build_filename(g_get_tmp_dir(), 
                                G_DIR_SEPARATOR_S, tmp, NULL);
    g_free(tmp);

    return(filename);
}
