summaryrefslogtreecommitdiff
path: root/src/pmchart/view.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmchart/view.cpp')
-rw-r--r--src/pmchart/view.cpp1313
1 files changed, 1313 insertions, 0 deletions
diff --git a/src/pmchart/view.cpp b/src/pmchart/view.cpp
new file mode 100644
index 0000000..fcb6cf0
--- /dev/null
+++ b/src/pmchart/view.cpp
@@ -0,0 +1,1313 @@
+/*
+ * Copyright (c) 2006, Ken McDonell. All Rights Reserved.
+ * Copyright (c) 2013, Red Hat Inc.
+ *
+ * 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 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.
+ */
+#include <QtCore/QString>
+#include <QtGui/QMessageBox>
+#include <QtGui/QColor>
+
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include "main.h"
+#include "openviewdialog.h"
+#include "saveviewdialog.h"
+
+/*
+ * View file parsing routines and global variables follow. These are
+ * currently not part of any class, and may need to be reworked a bit
+ * to use more portable (Qt) file IO interfaces (for a Windows port).
+ */
+static char _fname[MAXPATHLEN];
+static uint _line;
+static uint _errors;
+static uint _width;
+static uint _height;
+static uint _points;
+static uint _xpos;
+static uint _ypos;
+
+#define MAXWDSZ 256
+
+// parser states
+#define S_BEGIN 0
+#define S_VERSION 1
+#define S_TOP 2
+#define S_CHART 3
+#define S_PLOT 4
+
+// config file styles (mode)
+#define M_UNKNOWN 0
+#define M_PMCHART 1
+#define M_KMCHART 2
+
+// global attributes
+#define G_WIDTH 1
+#define G_HEIGHT 2
+#define G_POINTS 3
+#define G_XPOS 4
+#define G_YPOS 5
+
+// host mode
+#define H_DYNAMIC 1
+#define H_LITERAL 2
+
+// error severity
+#define E_INFO 0
+#define E_CRIT 1
+#define E_WARN 2
+
+// version numbers we're willing and able to support
+#define P1_1 101
+#define P1_2 102
+#define P2_0 200
+#define P2_1 201
+#define K1 1
+
+// instance / matching / not-matching
+#define IM_NONE 0
+#define IM_INST 1
+#define IM_MATCH 2
+#define IM_NOT_MATCH 3
+
+const char *_style[] = { "None", "Line", "Bar", "Stack", "Area", "Util" };
+#define stylestr(x) _style[(int)x]
+
+static void err(int severity, int do_where, QString msg)
+{
+ if (do_where) {
+ QString where = QString();
+ where.sprintf("%s[%d] ", _fname, _line);
+ msg.prepend(where);
+ }
+ if (Cflag) {
+ if (severity == E_CRIT)
+ msg.prepend("Error: ");
+ else if (severity == E_WARN)
+ msg.prepend("Warning: ");
+ // else do nothing for E_INFO
+ msg.append("\n");
+ fflush(stderr);
+ pmprintf("%s", (const char *)msg.toAscii());
+ pmflush();
+ }
+ else {
+ if (severity == E_CRIT)
+ QMessageBox::critical(pmchart, pmProgname, msg);
+ else if (severity == E_WARN)
+ QMessageBox::warning(pmchart, pmProgname, msg);
+ else
+ QMessageBox::information(pmchart, pmProgname, msg);
+ }
+ _errors++;
+}
+
+static char *getwd(FILE *f)
+{
+ static char buf[MAXWDSZ];
+ static int lastc = 0;
+ char *p;
+ int c;
+ int quote = 0;
+
+ if ((char)lastc == '\n') {
+eol:
+ buf[0] = '\n';
+ buf[1] = '\0';
+ _line++;
+ lastc = 0;
+ goto done;
+ }
+
+ // skip to first non-white space
+ p = buf;
+ while ((c = fgetc(f)) != EOF) {
+ if ((char)c == '\n')
+ goto eol;
+ if (!isspace((char)c)) {
+ // got one
+ if ((char)c == '"') {
+ quote = 1;
+ }
+ else {
+ *p++ = c;
+ }
+ break;
+ }
+ }
+ if (feof(f)) return NULL;
+
+ for ( ; p < &buf[MAXWDSZ]; ) {
+ if ((c = fgetc(f)) == EOF) break;
+ if ((char)c == '\n') {
+ lastc = c;
+ break;
+ }
+ if (quote == 0 && isspace((char)c)) break;
+ if (quote && (char)c == '"') break;
+ *p++ = c;
+ }
+
+ if (p == &buf[MAXWDSZ]) {
+ QString msg = QString();
+ p[-1] = '\0';
+ msg.sprintf("Word truncated after %d characters!\n\"%20.20s ... %20.20s\"", (int)sizeof(buf)-1, buf, &p[-21]);
+ err(E_CRIT, true, msg);
+ }
+ else
+ *p = '\0';
+
+done:
+ if ((pmDebug & DBG_TRACE_APPL0) && (pmDebug & DBG_TRACE_APPL2)) {
+ if (buf[0] == '\n')
+ fprintf(stderr, "openView getwd=EOL\n");
+ else
+ fprintf(stderr, "openView getwd=\"%s\"\n", buf);
+ }
+
+ return buf;
+}
+
+static void eol(FILE *f)
+{
+ char *w;
+
+ while ((w = getwd(f)) != NULL && w[0] != '\n') {
+ QString msg = QString("Syntax error: unexpected word \"");
+ msg.append(w);
+ msg.append("\"");
+ err(E_CRIT, true, msg);
+ }
+}
+
+static void skip2eol(FILE *f)
+{
+ char *w;
+
+ while ((w = getwd(f)) != NULL && w[0] != '\n') {
+ ;
+ }
+}
+
+static void xpect(const char *want, const char *got)
+{
+ QString msg = QString("Syntax error: expecting \"");
+ msg.append(want);
+ msg.append("\", found ");
+ if (got == NULL)
+ msg.append("End-of-File");
+ else if (got[0] == '\n')
+ msg.append("End-of-Line");
+ else {
+ msg.append("\"");
+ msg.append(got);
+ msg.append("\"");
+ }
+ err(E_CRIT, true, msg);
+}
+
+static QColor colorSpec(QString colorName, int *sequence)
+{
+ if (colorName == "#-cycle")
+ return nextColor("#-cycle", sequence);
+ if (ColorScheme::lookupScheme(colorName) == true)
+ return nextColor(colorName, sequence);
+ QColor color = ColorScheme::colorSpec(colorName);
+ if (!color.isValid()) {
+ QString errmsg;
+ errmsg.append("Invalid color name: ");
+ errmsg.append(colorName);
+ err(E_CRIT, true, errmsg);
+ color = Qt::white;
+ }
+ return color;
+}
+
+void OpenViewDialog::globals(int *w, int *h, int *pts, int *x, int *y)
+{
+ // Note: we use global variables here so that all views specified
+ // on the command line get input into these values (the maximum
+ // observed value is always used), without clobbering each other.
+ //
+ *w = _width;
+ *h = _height;
+ *pts = _points;
+ *x = _xpos;
+ *y = _ypos;
+}
+
+bool OpenViewDialog::openView(const char *path)
+{
+ Tab *tab;
+ Chart *cp = NULL;
+ int ct = pmchart->tabWidget()->currentIndex();
+ int m;
+ ColorScheme scheme;
+ FILE *f;
+ int is_popen = 0;
+ char *w;
+ int state = S_BEGIN;
+ int mode = M_UNKNOWN;
+ int h_mode;
+ int version;
+ QString errmsg;
+ QRegExp regex;
+ int sep = __pmPathSeparator();
+ int sts = 0;
+
+ if (strcmp(path, "-") == 0) {
+ f = stdin;
+ strcpy(_fname, "stdin");
+ }
+ else if (path[0] == '/') {
+ strcpy(_fname, path);
+ if ((f = fopen(_fname, "r")) == NULL)
+ goto noview;
+ }
+ else {
+ QString homepath = QDir::toNativeSeparators(QDir::homePath());
+
+ strcpy(_fname, path);
+ if ((f = fopen(_fname, "r")) == NULL) {
+ // not found, start the great hunt
+ // try user's pmchart dir ...
+ snprintf(_fname, sizeof(_fname),
+ "%s%c" ".pcp%c" "pmchart%c" "%s",
+ (const char *)homepath.toAscii(), sep, sep, sep, path);
+ if ((f = fopen(_fname, "r")) == NULL) {
+ // try system pmchart dir
+ snprintf(_fname, sizeof(_fname),
+ "%s%c" "config%c" "pmchart%c" "%s",
+ pmGetConfig("PCP_VAR_DIR"), sep, sep, sep, path);
+ if ((f = fopen(_fname, "r")) == NULL) {
+ // try user's kmchart dir
+ snprintf(_fname, sizeof(_fname),
+ "%s%c" ".pcp%c" "kmchart%c" "%s",
+ (const char *)homepath.toAscii(),
+ sep, sep, sep, path);
+ if ((f = fopen(_fname, "r")) == NULL) {
+ // try system kmchart dir
+ snprintf(_fname, sizeof(_fname),
+ "%s%c" "config%c" "kmchart%c" "%s",
+ pmGetConfig("PCP_VAR_DIR"),
+ sep, sep, sep, path);
+ if ((f = fopen(_fname, "r")) == NULL) {
+ snprintf(_fname, sizeof(_fname),
+ "%s%c" "config%c" "pmchart%c" "%s",
+ pmGetConfig("PCP_VAR_DIR"),
+ sep, sep, sep, path);
+ goto noview;
+ }
+ }
+ }
+ }
+ }
+ // check for executable and popen() as needed
+ //
+ if (fgetc(f) == '#' && fgetc(f) == '!') {
+ char cmd[MAXPATHLEN];
+ sprintf(cmd, "%s", _fname);
+ fclose(f);
+ if ((f = popen(cmd, "r")) == NULL)
+ goto nopipe;
+ is_popen = 1;
+ }
+ else {
+ rewind(f);
+ }
+ }
+
+ _line = 1;
+ _errors = 0;
+ console->post(PmChart::DebugView, "Load View: %s", _fname);
+
+ while ((w = getwd(f)) != NULL) {
+ if (state == S_BEGIN) {
+ // expect #pmchart
+ if (strcasecmp(w, "#pmchart") == 0)
+ mode = M_PMCHART;
+ else if (strcasecmp(w, "#kmchart") == 0)
+ mode = M_KMCHART;
+ else {
+ xpect("#pmchart\" or \"#kmchart", w);
+ goto abandon;
+ }
+ eol(f);
+ state = S_VERSION;
+ continue;
+ }
+
+ if (w[0] == '\n')
+ // skip empty lines and comments
+ continue;
+
+ if (w[0] == '#') {
+ // and comments
+ skip2eol(f);
+ continue;
+ }
+
+ if (state == S_VERSION) {
+ // expect version X.X host [dynamic|static]
+ if (strcasecmp(w, "version") != 0) {
+ xpect("version", w);
+ goto abandon;
+ }
+ w = getwd(f);
+ if (w == NULL || w[0] == '\n') {
+ xpect("<version number>", w);
+ goto abandon;
+ }
+ version = 0;
+ if (mode == M_PMCHART) {
+ if (strcmp(w, "2.1") == 0)
+ version = P2_1;
+ else if (strcmp(w, "2.0") == 0)
+ version = P2_0;
+ else if (strcmp(w, "1.1") == 0)
+ version = P1_1;
+ else if (strcmp(w, "1.2") == 0)
+ version = P1_2;
+ }
+ else if (mode == M_KMCHART) {
+ if (strcmp(w, "1") == 0)
+ version = K1;
+ }
+ if (version == 0) {
+ xpect("<version number>", w);
+ goto abandon;
+ }
+ w = getwd(f);
+ if (w == NULL || w[0] == '\n') {
+ if (mode == M_KMCHART) {
+ // host [literal|dynamic] is optional for new pmchart
+ h_mode = H_DYNAMIC;
+ state = S_TOP;
+ continue;
+ }
+ else {
+ xpect("host", w);
+ goto abandon;
+ }
+ }
+ if (strcasecmp(w, "host") != 0) {
+ xpect("host", w);
+ goto abandon;
+ }
+ w = getwd(f);
+ if (w != NULL && strcasecmp(w, "literal") == 0) {
+ h_mode = H_LITERAL;
+ }
+ else if (w != NULL && strcasecmp(w, "dynamic") == 0) {
+ h_mode = H_DYNAMIC;
+ }
+ else {
+ xpect("literal\" or \"dynamic", w);
+ goto abandon;
+ }
+ eol(f);
+ state = S_TOP;
+ }
+
+ else if (state == S_TOP) {
+ if (strcasecmp(w, "chart") == 0) {
+new_chart:
+ char *title = NULL;
+ Chart::Style style = Chart::NoStyle;
+ int autoscale = 1;
+ char *endnum;
+ double ymin = 0;
+ double ymax = 0;
+ int legend = 1;
+ int antialias = 1;
+
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("title\" or \"style", w);
+ goto abort_chart;
+ }
+ if (strcasecmp(w, "title") == 0) {
+ // optional title "<title>"
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<title>", w);
+ goto abort_chart;
+ }
+ if ((title = strdup(w)) == NULL) nomem();
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("style", w);
+ goto abort_chart;
+ }
+ }
+ if (strcasecmp(w, "style") == 0) {
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<chart style>", w);
+ goto abort_chart;
+ }
+ if (strcasecmp(w, "plot") == 0)
+ style = Chart::LineStyle;
+ else if (strcasecmp(w, "line") == 0)
+ style = Chart::LineStyle;
+ else if (strcasecmp(w, "bar") == 0)
+ style = Chart::BarStyle;
+ else if (strcasecmp(w, "stacking") == 0)
+ style = Chart::StackStyle;
+ else if (strcasecmp(w, "area") == 0)
+ style = Chart::AreaStyle;
+ else if (strcasecmp(w, "utilization") == 0)
+ style = Chart::UtilisationStyle;
+ else if (strcasecmp(w, "event") == 0)
+ style = Chart::EventStyle;
+ else {
+ xpect("<chart style>", w);
+ goto abort_chart;
+ }
+ }
+
+ // down to the optional bits
+ // - scale
+ // - legend
+ if ((w = getwd(f)) == NULL || w[0] == '\n')
+ goto done_chart;
+ if (strcasecmp(w, "scale") == 0) {
+ // scale [from] ymin [to] ymax
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("from or <ymin>", w);
+ goto abort_chart;
+ }
+ if (strcasecmp(w, "from") == 0) {
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<ymin>", w);
+ goto abort_chart;
+ }
+ }
+ ymin = strtod(w, &endnum);
+ if (*endnum != '\0') {
+ xpect("<ymin>", w);
+ goto abort_chart;
+ }
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("to or <ymax>", w);
+ goto abort_chart;
+ }
+ if (strcasecmp(w, "to") == 0) {
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<ymax>", w);
+ goto abort_chart;
+ }
+ }
+ ymax = strtod(w, &endnum);
+ if (*endnum != '\0') {
+ xpect("<ymax>", w);
+ goto abort_chart;
+ }
+ autoscale = 0;
+ if ((w = getwd(f)) == NULL || w[0] == '\n')
+ goto done_chart;
+ }
+ if (strcasecmp(w, "legend") == 0) {
+ // optional legend on|off
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("on\" or \"off", w);
+ goto abort_chart;
+ }
+ if (strcasecmp(w, "on") == 0)
+ legend = 1;
+ else if (strcasecmp(w, "off") == 0)
+ legend = 0;
+ else {
+ xpect("on\" or \"off", w);
+ goto abort_chart;
+ }
+ if ((w = getwd(f)) == NULL || w[0] == '\n')
+ goto done_chart;
+ }
+ if (strcasecmp(w, "antialiasing") == 0) {
+ // optional antialiasing on|off
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("on\" or \"off", w);
+ goto abort_chart;
+ }
+ if (strcasecmp(w, "on") == 0)
+ antialias = 1;
+ else if (strcasecmp(w, "off") == 0)
+ antialias = 0;
+ else {
+ xpect("on\" or \"off", w);
+ goto abort_chart;
+ }
+ if ((w = getwd(f)) == NULL || w[0] == '\n')
+ goto done_chart;
+ }
+done_chart:
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "openView: new chart: style=%s",
+ stylestr(style));
+ if (title != NULL)
+ fprintf(stderr, " title=\"%s\"", title);
+ if (autoscale)
+ fprintf(stderr, " autoscale=yes");
+ else
+ fprintf(stderr, " ymin=%.1f ymax=%.1f", ymin, ymax);
+ if (legend)
+ fprintf(stderr, " legend=yes");
+ if (!antialias)
+ fprintf(stderr, " antialias=no");
+ fputc('\n', stderr);
+ }
+ if (Cflag == 0 || Cflag == 2) {
+ tab = pmchart->activeTab();
+ cp = new Chart(tab, tab->splitter());
+ cp->setStyle(style);
+ cp->setScheme(scheme.name());
+ if (title != NULL)
+ cp->changeTitle(title, mode == M_KMCHART);
+ if (legend == 0)
+ cp->setLegendVisible(false);
+ if (antialias == 0)
+ cp->setAntiAliasing(false);
+ cp->setScale(autoscale, ymin, ymax);
+ activeGroup->addGadget(cp);
+ tab->addGadget(cp);
+ }
+ state = S_CHART;
+ if (title != NULL) free(title);
+ continue;
+
+abort_chart:
+ // unrecoverable error in the chart clause of the view
+ // specification, abandon this one
+ if (title != NULL) free(title);
+ goto abandon;
+ }
+ else if (strcasecmp(w, "global") == 0) {
+ char *endnum;
+ uint value, attr;
+
+ // Global window attributes (geometry and visible points)
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("width\", \"height\", \"xpos\", \"ypos\", or \"points", w);
+ goto abandon;
+ }
+ else if (strcasecmp(w, "width") == 0)
+ attr = G_WIDTH;
+ else if (strcasecmp(w, "height") == 0)
+ attr = G_HEIGHT;
+ else if (strcasecmp(w, "points") == 0)
+ attr = G_POINTS;
+ else if (strcasecmp(w, "xpos") == 0)
+ attr = G_XPOS;
+ else if (strcasecmp(w, "ypos") == 0)
+ attr = G_YPOS;
+ else {
+ xpect("width\", \"height\", \"xpos\", \"ypos\", or \"points", w);
+ goto abandon;
+ }
+ w = getwd(f);
+ if (w == NULL || w[0] == '\n') {
+ xpect("<global attribute value>", w);
+ goto abandon;
+ }
+ value = (uint)strtoul(w, &endnum, 0);
+ if (*endnum != '\0') {
+ xpect("<global attribute value>", w);
+ goto abandon;
+ }
+ switch (attr) {
+ case G_WIDTH:
+ _width = qMax(_width, value);
+ break;
+ case G_HEIGHT:
+ _height = qMax(_height, value);
+ break;
+ case G_POINTS:
+ _points = qMax(_points, value);
+ break;
+ case G_XPOS:
+ _xpos = qMax(_xpos, value);
+ break;
+ case G_YPOS:
+ _ypos = qMax(_ypos, value);
+ break;
+ }
+ eol(f);
+ }
+ else if (strcasecmp(w, "scheme") == 0) {
+ //
+ // scheme <name> <color> <color>...
+ // provides finer-grained control over the color selections
+ // for an individual chart. The default color scheme is
+ // named #-cycle. A scheme can be used in place of a direct
+ // color name specification, and the color for a plot is
+ // then defined as the next unused color from that scheme.
+ //
+ w = getwd(f);
+ if (w == NULL || w[0] == '\n') {
+ xpect("<color scheme value>", w);
+ goto abandon;
+ }
+ else if (strcmp(w, "#-cycle") == 0) {
+ xpect("<non-default color scheme name>", w);
+ goto abandon;
+ }
+ if (ColorScheme::lookupScheme(w) == true) {
+ // duplicate - ignore (probably using a seen view again)
+ skip2eol(f);
+ continue;
+ }
+ scheme.setName(QString(w));
+ scheme.clear();
+ w = getwd(f);
+ while (w && w[0] != '\n') {
+ scheme.addColor(QString(w));
+ w = getwd(f);
+ }
+ if (scheme.size() < 2) {
+ xpect("<list of color names>", w);
+ goto abandon;
+ }
+ globalSettings.colorSchemes.append(scheme);
+ }
+ else if (strcasecmp(w, "view") == 0 || strcasecmp(w, "tab") == 0) {
+new_tab:
+ QString label, host;
+ int samples = globalSettings.sampleHistory;
+ int points = globalSettings.visibleHistory;
+ char *endnum;
+
+ w = getwd(f);
+ if (w == NULL || w[0] == '\n') {
+ xpect("<tab label>", w);
+ goto abandon;
+ }
+ label = w;
+
+ w = getwd(f);
+ if (w == NULL || w[0] == '\n')
+ goto done_tab;
+
+ // default "host" specification for the tab is optional
+ if (strcasecmp(w, "host") == 0) {
+ w = getwd(f);
+ if (w == NULL || w[0] == '\n') {
+ xpect("<host>", w);
+ goto abandon;
+ }
+ host = w;
+ w = getwd(f);
+ if (w == NULL || w[0] == '\n')
+ goto done_tab;
+ }
+ if (strcasecmp(w, "points") != 0) {
+ xpect("<tab points>", w);
+ goto abandon;
+ }
+ w = getwd(f);
+ if (w)
+ points = (uint)strtoul(w, &endnum, 0);
+ if (w == NULL || w[0] == '\n' || *endnum != '\0') {
+ xpect("<tab points value>", w);
+ goto abandon;
+ }
+
+ w = getwd(f);
+ if (w == NULL || w[0] == '\n')
+ goto done_tab;
+ if (strcasecmp(w, "samples") != 0) {
+ xpect("<tab samples>", w);
+ goto abandon;
+ }
+ w = getwd(f);
+ if (w)
+ samples = (uint)strtoul(w, &endnum, 0);
+ if (w == NULL || w[0] == '\n' || *endnum != '\0') {
+ xpect("<tab samples value>", w);
+ goto abandon;
+ }
+
+done_tab:
+ tab = pmchart->activeTab();
+ bool isArchive = tab->isArchiveSource();
+
+ if (host != QString::null) {
+ if (isArchive)
+ archiveGroup->use(PM_CONTEXT_ARCHIVE, host);
+ else
+ liveGroup->use(PM_CONTEXT_HOST, host);
+ }
+
+ if (tab->gadgetCount() == 0) { // edit the initial tab
+ TabWidget *tabWidget = pmchart->tabWidget();
+ tabWidget->setTabText(tabWidget->currentIndex(), label);
+ }
+ else { // create a completely new tab from scratch
+ tab = new Tab;
+ if (isArchive)
+ tab->init(pmchart->tabWidget(), archiveGroup, label);
+ else
+ tab->init(pmchart->tabWidget(), liveGroup, label);
+ pmchart->addActiveTab(tab);
+ }
+ activeGroup->setSampleHistory(samples);
+ activeGroup->setVisibleHistory(points);
+ }
+ else {
+ xpect("chart\", \"global\", \"scheme\" or \"tab", w);
+ goto abandon;
+ }
+ }
+
+ else if (state == S_CHART) {
+ int optional;
+ char *legend = NULL;
+ char *color = NULL;
+ char *host = NULL;
+ int inst_match_type = IM_NONE;
+ int numinst = -1;
+ int nextinst = -1;
+ int *instlist = NULL;
+ char **namelist = NULL;
+ pmMetricSpec pms;
+ int abort = 1; // default @ skip
+
+ memset(&pms, 0, sizeof(pms));
+ if (strcasecmp(w, "view") == 0 || strcasecmp(w, "tab") == 0) {
+ // new tab
+ state = S_TOP;
+ goto new_tab;
+ }
+ if (strcasecmp(w, "chart") == 0) {
+ // new chart
+ state = S_TOP;
+ goto new_chart;
+ }
+ if (strcasecmp(w, "plot") == 0) {
+ optional = 0;
+ }
+ else if (strcasecmp(w, "optional-plot") == 0) {
+ optional = 1;
+ }
+ else {
+ xpect("plot\" or \"optional-plot", w);
+ goto skip;
+ }
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("title\" or \"color", w);
+ goto skip;
+ }
+ if (strcasecmp(w, "title") == 0 ||
+ (mode == M_KMCHART && strcasecmp(w, "legend") == 0)) {
+ // optional title "<title>" or
+ // (for new pmchart) legend "<title>"
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<legend title>", w);
+ goto skip;
+ }
+ if ((legend = strdup(w)) == NULL) nomem();
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("color", w);
+ goto skip;
+ }
+ }
+ // color <color> is mandatory for pmchart, optional for
+ // new pmchart (where the default is color #-cycle)
+ if (strcasecmp(w, "color") == 0 || strcasecmp(w, "colour") == 0) {
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<color>", w);
+ goto skip;
+ }
+ if ((color = strdup(w)) == NULL) nomem();
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("host", w);
+ goto skip;
+ }
+ }
+ else if (mode == M_PMCHART) {
+ xpect("color", w);
+ goto skip;
+ }
+ // host <host> is mandatory for pmchart, optional for
+ // new pmchart (where the default is host *)
+ if (strcasecmp(w, "host") == 0) {
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<host>", w);
+ goto skip;
+ }
+ if (strcmp(w, "*") == 0)
+ host = NULL; // just like the new pmchart default
+ else {
+ if ((host = strdup(w)) == NULL) nomem();
+ }
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("metric", w);
+ goto skip;
+ }
+ }
+ else if (mode == M_PMCHART) {
+ xpect("host", w);
+ goto skip;
+ }
+ // metric is mandatory
+ if (strcasecmp(w, "metric") == 0) {
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<metric>", w);
+ goto skip;
+ }
+ if ((pms.metric = strdup(w)) == NULL) nomem();
+ }
+ else {
+ xpect("metric", w);
+ goto skip;
+ }
+ pms.ninst = 0;
+ pms.inst[0] = NULL;
+ if ((w = getwd(f)) != NULL && w[0] != '\n') {
+ // optional parts
+ // instance ...
+ // matching ...
+ // not-matching ...
+ if (strcasecmp(w, "instance") == 0) {
+ inst_match_type = IM_INST;
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<instance>", w);
+ goto skip;
+ }
+ pms.ninst = 1;
+ if ((pms.inst[0] = strdup(w)) == NULL) nomem();
+ }
+ else if (strcasecmp(w, "matching") == 0) {
+ inst_match_type = IM_MATCH;
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<pattern>", w);
+ goto skip;
+ }
+ pms.ninst = 1;
+ pms.inst[0] = strdup(w);
+ }
+ else if (strcasecmp(w, "not-matching") == 0) {
+ inst_match_type = IM_NOT_MATCH;
+ if ((w = getwd(f)) == NULL || w[0] == '\n') {
+ xpect("<pattern>", w);
+ goto skip;
+ }
+ pms.ninst = 1;
+ pms.inst[0] = strdup(w);
+ }
+ else {
+ xpect("instance\" or \"matching\" or \"not-matching", w);
+ goto skip;
+ }
+ if (mode == M_PMCHART) {
+ // pmchart has this lame "instance extends to end
+ // of line" syntax ... sigh
+ while ((w = getwd(f)) != NULL && w[0] != '\n') {
+ pms.inst[0] = (char *)realloc(pms.inst[0], strlen(pms.inst[0]) + strlen(w) + 2);
+ if (pms.inst[0] == NULL) nomem();
+ // if more than one space in the input, touch luck!
+ strcat(pms.inst[0], " ");
+ strcat(pms.inst[0], w);
+ }
+ if (pms.inst[0] != NULL) {
+ pms.ninst = 1;
+ }
+ }
+ else {
+ // expect end of line after instance/pattern
+ // (pmchart uses quotes to make instance a single
+ // lexical element in the line)
+ eol(f);
+ }
+ }
+
+ abort = 0;
+ if (pmDebug & DBG_TRACE_APPL2) {
+ fprintf(stderr, "openView: new %s", optional ? "optional-plot" : "plot");
+ if (legend != NULL) fprintf(stderr, " legend=\"%s\"", legend);
+ if (color != NULL) fprintf(stderr, " color=%s", color);
+ if (host != NULL) fprintf(stderr, " host=%s", host);
+ fprintf(stderr, " metric=%s", pms.metric);
+ if (pms.ninst == 1) {
+ fprintf(stderr, " inst=%s", pms.inst[0]);
+ if (inst_match_type == IM_NONE)
+ fprintf(stderr, " match=none (botch?)");
+ else if (inst_match_type == IM_INST)
+ fprintf(stderr, " match=instance");
+ else if (inst_match_type == IM_MATCH)
+ fprintf(stderr, " match=matching");
+ else if (inst_match_type == IM_NOT_MATCH)
+ fprintf(stderr, " match=not-matching");
+ }
+ fputc('\n', stderr);
+ }
+ if (Cflag == 0 || Cflag == 2) {
+ pms.isarch = (activeGroup == archiveGroup);
+ if (host != NULL) {
+ // host literal, add to the list of sources
+ if (activeGroup == archiveGroup) {
+ QString hostname = host;
+ if (archiveGroup->use(PM_CONTEXT_HOST, hostname) < 0) {
+ QString msg;
+ msg.sprintf("\nHost \"%s\" cannot be matched to an open archive for metric %s",
+ host, pms.metric);
+ errmsg.append(msg);
+ goto skip;
+ }
+ QmcSource source = archiveGroup->context()->source();
+ pms.source = strdup((const char *)
+ source.source().toAscii());
+ }
+ else {
+ pms.source = strdup(host);
+ }
+ }
+ else {
+ // no explicit host, use current default source
+ QmcSource source = activeGroup->context()->source();
+ pms.source = strdup((const char *)
+ source.source().toAscii());
+ }
+ // expand instances when not specified for metrics
+ // with instance domains and all instances required,
+ // or matching or not-matching instances required
+ //
+ if (inst_match_type != IM_INST) {
+ pmID pmid;
+ pmDesc desc;
+
+ // if pmLookupName() or pmLookupDesc() fail, we'll
+ // notice in addPlot() and report the error below,
+ // so no need to do anything special here
+ //
+ if (pmLookupName(1, &pms.metric, &pmid) < 0)
+ goto try_plot;
+ if (pmLookupDesc(pmid, &desc) < 0)
+ goto try_plot;
+ if (desc.indom == PM_INDOM_NULL) {
+ if (inst_match_type == IM_MATCH ||
+ inst_match_type == IM_NOT_MATCH) {
+ // a bit embarrassing
+ QString msg = QString();
+ msg.sprintf("\nMetric \"%s\" for\n%s %s: no instance domain, cannot handle matching specification",
+ pms.metric, pms.isarch ? "archive" : "host",
+ pms.source);
+ errmsg.append(msg);
+ goto skip;
+ }
+ goto try_plot;
+ }
+
+ if (pms.isarch)
+ numinst = pmGetInDomArchive(desc.indom, &instlist, &namelist);
+ else
+ numinst = pmGetInDom(desc.indom, &instlist, &namelist);
+ if (numinst < 0) {
+ QString msg = QString();
+ msg.sprintf("\nMetric \"%s\" for\n%s %s: empty instance domain",
+ pms.metric, pms.isarch ? "archive" : "host",
+ pms.source);
+ errmsg.append(msg);
+ goto skip;
+ }
+ if (inst_match_type != IM_NONE) {
+ regex.setPattern(pms.inst[0]);
+ if (!regex.isValid()) {
+ errmsg = "Invalid regular expression:\n ";
+ errmsg.append(pms.inst[0]);
+ errmsg.append("\n\n");
+ errmsg.append(regex.errorString());
+ goto skip;
+ }
+ }
+ pms.ninst = numinst ? 1 : 0;
+ if (pms.inst[0] != NULL) {
+ free(pms.inst[0]);
+ pms.inst[0] = NULL;
+ }
+ }
+
+try_plot:
+ if (numinst > 0) {
+ pms.inst[0] = NULL;
+ for (nextinst++ ; nextinst < numinst; nextinst++) {
+ sts = 0;
+ if (inst_match_type == IM_MATCH ||
+ inst_match_type == IM_NOT_MATCH) {
+ sts = regex.indexIn(QString(namelist[nextinst]));
+ }
+ switch (inst_match_type) {
+ case IM_MATCH:
+ if (sts != -1)
+ pms.inst[0] = namelist[nextinst];
+ break;
+ case IM_NOT_MATCH:
+ if (sts == -1)
+ pms.inst[0] = namelist[nextinst];
+ break;
+ case IM_NONE:
+ pms.inst[0] = namelist[nextinst];
+ break;
+ }
+ if (pms.inst[0] != NULL)
+ break;
+ }
+ if (nextinst == numinst)
+ goto skip;
+ }
+ m = cp->addItem(&pms, QString(legend));
+ if (m < 0) {
+ if (!optional) {
+ QString msg;
+ if (pms.inst[0] != NULL)
+ msg.sprintf("\nFailed to plot metric \"%s[%s]\" for\n%s %s:\n",
+ pms.metric, pms.inst[0],
+ pms.isarch ? "archive" : "host",
+ pms.source);
+ else
+ msg.sprintf("\nFailed to plot metric \"%s\" for\n%s %s:\n",
+ pms.metric, pms.isarch ? "archive" : "host",
+ pms.source);
+ if (m == PM_ERR_CONV) {
+ msg.append("Units for this metric are not compatible with other plots in this chart");
+ }
+ else
+ msg.append(pmErrStr(m));
+ errmsg.append(msg);
+ }
+ }
+ else if (color != NULL && strcmp(color, "#-cycle") != 0) {
+ int seq = cp->sequence();
+ cp->setStroke(m, cp->style(), colorSpec(color, &seq));
+ cp->setSequence(seq);
+ }
+ if (numinst > 0)
+ // more instances still to be processed for this metric
+ goto try_plot;
+
+ }
+
+skip:
+ if (legend != NULL) free(legend);
+ if (color != NULL) free(color);
+ if (host != NULL) free(host);
+ if (instlist != NULL) free(instlist);
+ if (namelist != NULL) free(namelist);
+ if (pms.source != NULL) free(pms.source);
+ if (pms.metric != NULL) free(pms.metric);
+ if (pms.inst[0] != NULL) free(pms.inst[0]);
+
+ if (abort)
+ goto abandon;
+
+ continue;
+ }
+
+ else {
+ QString msg = QString();
+ msg.sprintf("Botch, state=%d", state);
+ err(E_CRIT, true, msg);
+ goto abandon;
+ }
+
+ continue;
+
+abandon:
+ // giving up on the whole view specification
+ break;
+
+ }
+
+ if (!errmsg.isEmpty()) {
+ err(E_CRIT, true, errmsg);
+ }
+
+ if (f != stdin) {
+ if (is_popen)
+ pclose(f);
+ else
+ fclose(f);
+ }
+
+ if (_errors)
+ return false;
+
+ if (ct != pmchart->tabWidget()->currentIndex()) // new Tabs added
+ pmchart->setActiveTab(ct, true);
+
+ if ((Cflag == 0 || Cflag == 2) && cp != NULL) {
+ activeGroup->setupWorldView();
+ pmchart->activeTab()->showGadgets();
+ }
+ return true;
+
+noview:
+ errmsg = QString("Cannot open view file \"");
+ errmsg.append(_fname);
+ errmsg.append("\"\n");
+ errmsg.append(strerror(errno));
+ err(E_CRIT, false, errmsg);
+ return false;
+
+nopipe:
+ errmsg.sprintf("Cannot execute \"%s\"\n%s", _fname, strerror(errno));
+ err(E_CRIT, false, errmsg);
+ return false;
+}
+
+void SaveViewDialog::setGlobals(int width, int height, int points, int x, int y)
+{
+ _width = width;
+ _height = height;
+ _points = points;
+ _xpos = x;
+ _ypos = y;
+}
+
+static void saveScheme(FILE *f, QString scheme)
+{
+ ColorScheme *cs = ColorScheme::findScheme(scheme);
+ int m;
+
+ if (cs) {
+ fprintf(f, "scheme %s", (const char *)cs->name().toAscii());
+ for (m = 0; m < cs->size(); m++)
+ fprintf(f, " %s", (const char *)cs->colorName(m).toAscii());
+ fprintf(f, "\n\n");
+ }
+}
+
+void SaveViewDialog::saveChart(FILE *f, Chart *cp, bool hostDynamic)
+{
+ const char *s;
+ double ymin, ymax;
+ bool autoscale;
+
+ fprintf(f, "chart");
+ if (cp->title() != QString::null)
+ fprintf(f, " title \"%s\"", (const char*)cp->title().toAscii());
+ switch (cp->style()) {
+ case Chart::LineStyle:
+ s = "plot";
+ break;
+ case Chart::BarStyle:
+ s = "bar";
+ break;
+ case Chart::StackStyle:
+ s ="stacking";
+ break;
+ case Chart::AreaStyle:
+ s = "area";
+ break;
+ case Chart::UtilisationStyle:
+ s = "utilization";
+ break;
+ case Chart::EventStyle:
+ s = "event";
+ break;
+ case Chart::NoStyle:
+ default:
+ s = "none";
+ break;
+ }
+ fprintf(f, " style %s", s);
+ if (cp->style() != Chart::UtilisationStyle) {
+ cp->scale(&autoscale, &ymin, &ymax);
+ if (!autoscale)
+ fprintf(f, " scale %f %f", ymin, ymax);
+ }
+ if (!cp->legendVisible())
+ fprintf(f, " legend off");
+ if (!cp->antiAliasing())
+ fprintf(f, " antialiasing off");
+ fputc('\n', f);
+ for (int m = 0; m < cp->metricCount(); m++) {
+ QString legend;
+ if (cp->activeItem(m) == false)
+ continue;
+ fprintf(f, "\tplot");
+ legend = cp->legend(m);
+ if (legend != QString::null)
+ fprintf(f, " legend \"%s\"", (const char *)legend.toAscii());
+ fprintf(f, " color %s", (const char *)cp->color(m).name().toAscii());
+ if (hostDynamic == false)
+ fprintf(f, " host %s", (const char *)
+ cp->metricContext(m)->source().host().toAscii());
+ fprintf(f, " metric %s", (const char *)
+ cp->metricName(m).toAscii());
+ if (cp->metric(m)->explicitInsts())
+ fprintf(f, " instance \"%s\"", (const char*)cp->metricInstance(m).toAscii());
+ fputc('\n', f);
+ }
+}
+
+bool SaveViewDialog::saveView(QString file, bool hostDynamic,
+ bool sizeDynamic, bool allTabs, bool allCharts)
+{
+ FILE *f;
+ int c, t;
+ Tab *tab;
+ Gadget *gadget;
+ char *path = strdup((const char *)file.toAscii());
+ QStringList schemes;
+
+ if ((f = fopen(path, "w")) == NULL)
+ goto noview;
+
+ fprintf(f, "#kmchart\nversion %d\n\n", K1);
+ if (sizeDynamic == false) {
+ fprintf(f, "global width %u\n", _width);
+ fprintf(f, "global height %u\n", _height);
+ fprintf(f, "global points %u\n", _points);
+ fprintf(f, "global xpos %u\n", _xpos);
+ fprintf(f, "global ypos %u\n", _ypos);
+ fprintf(f, "\n");
+ }
+
+ for (c = 0; c < pmchart->activeTab()->gadgetCount(); c++) {
+ gadget = pmchart->activeTab()->gadget(c);
+ if (gadget->scheme() == QString::null ||
+ schemes.contains(gadget->scheme()) == true)
+ continue;
+ schemes.append(gadget->scheme());
+ }
+ for (c = 0; c < schemes.size(); c++)
+ saveScheme(f, schemes.at(c));
+
+ if (allTabs) {
+ TabWidget *tabWidget = pmchart->tabWidget();
+ for (t = 0; t < tabWidget->size(); t++) {
+ tab = tabWidget->at(t);
+ fprintf(f, "\ntab \"%s\"\n\n",
+ (const char *) tabWidget->tabText(t).toAscii());
+ for (c = 0; c < tab->gadgetCount(); c++)
+ tab->gadget(c)->save(f, hostDynamic);
+ }
+ }
+ else {
+ tab = pmchart->activeTab();
+ if (!allCharts)
+ tab->currentGadget()->save(f, hostDynamic);
+ else
+ for (c = 0; c < tab->gadgetCount(); c++)
+ tab->gadget(c)->save(f, hostDynamic);
+ }
+
+ fflush(f);
+ fclose(f);
+ free (path);
+ return true;
+
+noview:
+ QString errmsg;
+ errmsg.sprintf("Cannot open \"%s\" for writing\n%s", path, strerror(errno));
+ err(E_CRIT, false, errmsg);
+ free (path);
+ return false;
+}