summaryrefslogtreecommitdiff
path: root/src/pmgadgets/parse.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmgadgets/parse.cpp')
-rw-r--r--src/pmgadgets/parse.cpp1339
1 files changed, 1339 insertions, 0 deletions
diff --git a/src/pmgadgets/parse.cpp b/src/pmgadgets/parse.cpp
new file mode 100644
index 0000000..923baef
--- /dev/null
+++ b/src/pmgadgets/parse.cpp
@@ -0,0 +1,1339 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 1996 Silicon Graphics, 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 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 <pcp/pmapi.h>
+#include <pcp/impl.h>
+#include <setjmp.h>
+#include <values.h>
+#include "global.h"
+#include "tokens.h"
+
+#include "qed.h"
+#include "qed_bar.h"
+#include "qed_led.h"
+#include "qed_line.h"
+#include "qed_label.h"
+#include "qed_legend.h"
+#include "qed_gadget.h"
+#include "qed_colorlist.h"
+#include "qed_actionlist.h"
+#include <qnumeric.h>
+
+extern "C" {
+int yylex();
+}
+
+// Variables manipulated by the lexical analyser
+//
+unsigned nLines = 1;
+int tokenIntVal;
+double tokenRealVal;
+char *tokenStringVal;
+
+// Parser variables
+//
+static int token;
+static int eofOK = 1;
+static unsigned nErrors;
+static unsigned nWarnings;
+
+static QList<QedColorList*> colorLists;
+static QList<QedLegend*> legends;
+static QList<QedActionList*> actionLists;
+static QList<QedGadget*> gadgets;
+static QWidget *parent;
+
+class MetricData
+{
+public:
+ MetricData(char* h, char* m, char* i) { host = h; metric = m; inst = i; }
+ char *host; // NULL => localhost
+ char *metric; // metric name
+ char *inst; // instance name (may be NULL)
+};
+
+int AddMetrics(double a, QList<MetricData*>&b, QedGadget *c) { return 0; /* TODO */ }
+
+typedef struct {
+ int id;
+ const char* string;
+} TokenList;
+
+static TokenList tokenStrings[] = {
+ { TOK_LINE, "line" },
+ { TOK_LABEL, "label" },
+ { TOK_BAR, "bar" },
+ { TOK_MULTIBAR, "multibar" },
+ { TOK_BARGRAPH, "bargraph" },
+ { TOK_LED, "led" },
+
+ { TOK_LEGEND, "legend" },
+ { TOK_COLOURLIST, "colourlist" },
+ { TOK_ACTIONLIST, "actionlist" },
+
+ { TOK_BAD_RES_WORD, "<bad reserved word>" },
+ { TOK_UPDATE, "update" },
+ { TOK_METRIC, "metric" },
+ { TOK_HORIZONTAL, "horizontal" },
+ { TOK_VERTICAL, "vertical" },
+ { TOK_METRICS, "metrics" },
+ { TOK_MIN, "min" },
+ { TOK_MAX, "max" },
+ { TOK_DEFAULT, "default" },
+ { TOK_FIXED, "fixed" },
+ { TOK_COLOUR, "colour" },
+ { TOK_HISTORY, "history" },
+ { TOK_NOBORDER, "noborder" },
+
+ { TOK_IDENTIFIER, "<identifier>" },
+ { TOK_INTEGER, "<integer>" },
+ { TOK_REAL, "<real>" },
+ { TOK_STRING, "<string>" },
+ { TOK_LPAREN, "(" },
+ { TOK_RPAREN, ")" },
+ { TOK_LBRACKET, "[" },
+ { TOK_RBRACKET, "]" },
+ { TOK_COLON, ":" },
+
+ { TOK_EOF, "EOF" },
+ { 0, "raw EOF" },
+};
+const unsigned nTokenStrings = sizeof(tokenStrings) / sizeof(tokenStrings[0]);
+
+static const char*
+TokenToString(int tok)
+{
+ for (unsigned i = 0; i < nTokenStrings; i++)
+ if (tokenStrings[i].id == tok)
+ return tokenStrings[i].string;
+ return "???";
+}
+
+static jmp_buf scannerEofEnv;
+static int stashedToken = -1;
+
+static int
+NextToken()
+{
+ static int atEOF = 0;
+
+ if (atEOF)
+ return token;
+
+ if (stashedToken < 0) {
+ if ((token = yylex()) == 0) {
+ atEOF = 1;
+ token = TOK_EOF;
+ if (!eofOK) {
+ nErrors++;
+ pmprintf("Error, line %d: unexpected EOF, giving up\n", nLines);
+ longjmp(scannerEofEnv, 0);
+ }
+ }
+ }
+ else {
+ token = stashedToken;
+ stashedToken = -1;
+ }
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "input: %s (%d)\n", TokenToString(token), token);
+ return token;
+}
+
+static void
+FindNewStatement()
+{
+ eofOK = 1;
+ for(;;)
+ switch (token) {
+ case TOK_EOF:
+ case TOK_LINE:
+ case TOK_LABEL:
+ case TOK_BAR:
+ case TOK_MULTIBAR:
+ case TOK_BARGRAPH:
+ case TOK_LED:
+ case TOK_LEGEND:
+ case TOK_COLOURLIST:
+ return;
+
+ default:
+ NextToken();
+ break;
+ }
+}
+
+static int
+ParseUpdate(double& update, int eofMayFollow)
+{
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_INTEGER)
+ update = tokenIntVal;
+ else if (token == TOK_REAL)
+ update = tokenRealVal;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: update interval must be a number\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseHistory(int& history, int eofMayFollow)
+{
+ int err = 0;
+
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_INTEGER) {
+ if ((history = tokenIntVal) < 0)
+ err = 1;
+ }
+ else
+ err = 1;
+
+ if (err) {
+ nErrors++;
+ pmprintf("Error, line %d: history must be a positive integer\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParsePosition(int& x, int& y, int eofMayFollow)
+{
+ eofOK = 0;
+ if (token == TOK_INTEGER)
+ x = tokenIntVal * appData.zoom;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: X coordinate must be an integer\n", nLines);
+ return -1;
+ }
+ NextToken();
+ if (token == TOK_INTEGER)
+ y = tokenIntVal * appData.zoom;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: Y coordinate must be an integer\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseSize(unsigned& w, unsigned& h, int eofMayFollow)
+{
+ eofOK = 0;
+ if (token == TOK_INTEGER && tokenIntVal > 0)
+ w = tokenIntVal * appData.zoom;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: width must be a positive integer\n", nLines);
+ return -1;
+ }
+ NextToken();
+ if (token == TOK_INTEGER && tokenIntVal > 0)
+ h = tokenIntVal * appData.zoom;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: height must be a positive integer\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseGeometry(int& x, int& y, unsigned& w, unsigned& h, int eofMayFollow)
+{
+ int sts;
+
+ eofOK = 0;
+ if ((sts = ParsePosition(x, y, 0)) < 0)
+ return sts;
+ sts = ParseSize(w, h, eofMayFollow);
+ return sts;
+}
+
+static int
+ParseActionListActions(QedActionList *alp, int eofMayFollow)
+{
+ unsigned int n = 0;
+
+ eofOK = 0;
+ NextToken();
+ while (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ if (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ alp->addName(tokenStringVal);
+ NextToken();
+ }
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: action name expected\n", nLines);
+ return -1;
+ }
+ if (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ alp->addAction(tokenStringVal);
+ NextToken();
+ }
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: action expected\n", nLines);
+ return -1;
+ }
+ if (token == TOK_DEFAULT) {
+ if (alp->defaultPos() == -1)
+ alp->setDefaultPos(n);
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: only one default action permitted in list\n", nLines);
+ }
+ NextToken();
+ }
+ n++;
+ }
+ eofOK = eofMayFollow;
+ if (token == TOK_RPAREN)
+ NextToken();
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: `)' expected at end of action list\n", nLines);
+ return -1;
+ }
+ return 0;
+}
+
+typedef enum {
+ AL_NEED_NAME = 1,
+ AL_MAY_BE_REFERENCE = 2
+} ActionListFlags;
+
+// This parses 3 types of action list:
+//
+// _actions name (...) definition of action list
+// _actions name reference to defined action list
+// _actions (...) anonymous action list
+//
+static int
+ParseActionList(
+ int flags, // AL_* flags above
+ int eofMayFollow)
+{
+ char *name;
+ char anon[64];
+ int ai, sts;
+ int existingActions = -1;
+ QedActionList *alp;
+ static int count;
+
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_IDENTIFIER) {
+ name = tokenStringVal;
+ for (ai = 0; ai < actionLists.size(); ai++) {
+ alp = actionLists.at(ai);
+ if (strcmp(alp->identity(), name) == 0)
+ break;
+ }
+ if (ai != actionLists.size())
+ existingActions = ai;
+ else
+ alp = new QedActionList(name);
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else {
+ if (flags & AL_NEED_NAME) {
+ nErrors++;
+ pmprintf("Error, line %d: name expected for actions list\n", nLines);
+ return -1;
+ }
+ snprintf(anon, sizeof(anon), "anonymous#%d", count++);
+ alp = new QedActionList(anon);
+ name = anon;
+ }
+
+ sts = 0;
+ if (token == TOK_LPAREN) {
+ // This is an action list definition (the name may be omitted)
+ //
+ eofOK = 0;
+ if (existingActions != -1) {
+ nErrors++;
+ pmprintf("Error, line %d: an actions list named \"%s\" already exists\n",
+ nLines, name);
+ return -1;
+ }
+ sts = ParseActionListActions(alp, eofMayFollow);
+ }
+ else {
+ // This should be a reference to an already defined action list
+ //
+ if (name == NULL) {
+ nErrors++;
+ pmprintf("Error, line %d: name or ( expected for actions list\n", nLines);
+ return -1;
+ }
+ if (!(flags & AL_MAY_BE_REFERENCE)) {
+ nErrors++;
+ pmprintf("Error, line %d: `(' expected for actions list definition\n", nLines);
+ return -1;
+ }
+ if (existingActions == -1) {
+ nErrors++;
+ pmprintf("Error, line %d: no action list named %s defined\n",
+ nLines, name);
+ return -1;
+ }
+ }
+
+ if (sts >= 0 && existingActions == -1)
+ actionLists.append(alp);
+
+ return sts;
+}
+
+static int
+ParseLine(int eofMayFollow)
+{
+ int x, y, sts;
+ unsigned w, h;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ nErrors++;
+ pmprintf("Error, line %d: lines may not have an update interval\n",
+ nLines);
+ return -1;
+ }
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedLine *l = new QedLine(parent, x, y, w, h);
+ if (l) {
+ gadgets.append(l);
+ } else {
+ perror("no memory for line gadget");
+ exit(1);
+ }
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ l->dump(stderr);
+ return 0;
+}
+
+static int
+ParseLabel(int eofMayFollow)
+{
+ int x, y, sts;
+ char* text;
+ char* font = appData.defaultFont;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParsePosition(x, y, 0);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ nErrors++;
+ pmprintf("Error, line %d: labels may not have an update interval\n",
+ nLines);
+ return -1;
+ }
+
+ if (token != TOK_STRING && token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: label string expected\n", nLines);
+ return -1;
+ }
+ text = tokenStringVal;
+ eofOK = eofMayFollow;
+ NextToken();
+ if (token == TOK_STRING || token == TOK_IDENTIFIER) {
+ font = tokenStringVal;
+ NextToken();
+ }
+
+ if (token == TOK_ACTIONLIST)
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+
+ if (sts >= 0) {
+ sts = 0;
+ // note: constructor specifies baseline pos
+ QedLabel* l = new QedLabel(parent, x, y, text, font);
+ if (l) {
+ gadgets.append(l);
+ } else {
+ perror("no memory for label gadget");
+ exit(1);
+ }
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ l->dump(stderr);
+ }
+
+ if (text)
+ free(text);
+ if (font && font != appData.defaultFont)
+ free(font);
+ return sts;
+}
+
+// A metric spec is one of
+//
+// <metric>
+// <metric>[<inst>]
+// <host>:<metric>
+// <host>:<metric>[<inst>]
+//
+static int
+ParseMetric(MetricData** resultPtr, int eofMayFollow)
+{
+ char* host;
+ char* metric;
+ char* inst;
+ char* hostOrMetric = NULL;
+
+ eofOK = 0;
+ if (token == TOK_IDENTIFIER)
+ hostOrMetric = tokenStringVal;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: performance metric expected\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ if (token == TOK_COLON) { // Have "<host>:<metric>"
+ host = hostOrMetric;
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_IDENTIFIER)
+ metric = tokenStringVal;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: performance metric expected\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else { // Optional "<host>:" omitted
+ host = NULL;
+ metric = hostOrMetric;
+ }
+ if (token == TOK_LBRACKET) {
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_IDENTIFIER || token == TOK_STRING)
+ inst = tokenStringVal;
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: performance metric instance expected\n",
+ nLines);
+ return -1;
+ }
+ NextToken();
+ if (token != TOK_RBRACKET) {
+ nErrors++;
+ pmprintf("Error, line %d: `]' for performance metric instance missing\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else
+ inst = NULL;
+
+ *resultPtr = new MetricData(host, metric, inst);
+ if (*resultPtr == NULL) {
+ perror("No memory for metric spec");
+ exit(1);
+ }
+ return 0;
+}
+
+static int
+ParseColor(char** colourName, int eofMayFollow)
+{
+ eofOK = 0;
+ NextToken();
+ if (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ *colourName = strdup(tokenStringVal);
+ if (*colourName == NULL) {
+ perror("No memory for color name");
+ exit(1);
+ }
+ }
+ else {
+ nErrors++;
+ pmprintf("Error, line %d: color name missing\n", nLines);
+ return -1;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseBar(int eofMayFollow)
+{
+ int x, y, sts;
+ unsigned w, h;
+ MetricData* metricData;
+ double min = qQNaN(), max = qQNaN();
+ int isVertical = 0;
+ double update = appData.delta;
+ char* colour = NULL;
+ int fixMax = 0;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ sts = ParseUpdate(update, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token != TOK_METRIC) {
+ nErrors++;
+ pmprintf("Error, line %d: _metric expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ sts = ParseMetric(&metricData, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ while (token == TOK_HORIZONTAL ||
+ token == TOK_VERTICAL ||
+ token == TOK_FIXED ||
+ token == TOK_MIN ||
+ token == TOK_MAX) {
+
+ if (token == TOK_FIXED) {
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_MAX) {
+ nErrors++;
+ pmprintf("Error, line %d: _fixed _max expected\n", nLines);
+ return -1;
+ }
+ fixMax = 1;
+ }
+
+ if (token == TOK_HORIZONTAL) {
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else if (token == TOK_VERTICAL) {
+ eofOK = eofMayFollow;
+ NextToken();
+ isVertical = 1;
+ }
+ else if (token == TOK_MIN) {
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: minimum must be a number\n", nLines);
+ return -1;
+ }
+ min = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ else if (token == TOK_MAX) {
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: maximum must be a number\n", nLines);
+ return -1;
+ }
+ max = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+ }
+
+ if (token == TOK_COLOUR) {
+ sts = ParseColor(&colour, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedBar *b = new QedBar(parent, x, y, w, h);
+ QList<MetricData*> mlp;
+ if (b) {
+ if (min != qQNaN())
+ b->setMinimum(min);
+ if (max != qQNaN())
+ b->setMaximum(max);
+ if (isVertical)
+ b->setOrientation(Qt::Vertical);
+ if (fixMax)
+ b->setScaleRange(0);
+ gadgets.append(b);
+ mlp.append(metricData);
+ } else {
+ perror("no memory for bar gadget");
+ exit(1);
+ }
+ if (colour)
+ b->setColor(colour);
+
+ if (AddMetrics(update, mlp, b))
+ nWarnings++;
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ b->dump(stderr);
+ return 0;
+}
+
+static int
+ParseMultibar(int eofMayFollow)
+{
+ int x, y, sts, ci;
+ unsigned w, h;
+ MetricData* metricData;
+ int isVertical = 0;
+ int noborder = 0;
+ int history = 0;
+ double update = appData.delta;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+
+ if (token == TOK_UPDATE) {
+ sts = ParseUpdate(update, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token == TOK_NOBORDER) {
+ noborder = 1;
+ NextToken();
+ }
+
+ if (token == TOK_HISTORY) {
+ sts = ParseHistory(history, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token != TOK_METRICS) {
+ nErrors++;
+ pmprintf("Error, line %d: _metrics (list) expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ if (token != TOK_LPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `(' expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ QList<MetricData*> mlp;
+ do {
+ sts = ParseMetric(&metricData, 0);
+ if (sts < 0)
+ return sts;
+ mlp.append(metricData);
+ } while (token == TOK_IDENTIFIER);
+
+ if (token != TOK_RPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `)' expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ // By default a multibar has a floating maximum initially set to a small
+ // value. If a max of zero is specified, the multibar is a utilisation.
+ // The optional _fixed keyword before the maximum can be used to specify
+ // clipping.
+ //
+ int clip = 0;
+ double max = DBL_MAX;
+ if (token == TOK_FIXED) {
+ clip = 1;
+ NextToken();
+ if (token != TOK_MAX) {
+ nErrors++;
+ pmprintf("Error, line %d: _maximum expected\n", nLines);
+ return -1;
+ }
+ }
+ if (token == TOK_MAX) {
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: a maximum must be a number\n", nLines);
+ return -1;
+ }
+ max = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+ if (max < 0.0 || (clip && max == 0.0)) {
+ nErrors++;
+ pmprintf("Error, line %d: a fixed maximum must be positive\n",
+ nLines);
+ return -1;
+ }
+ NextToken();
+ }
+
+ if (token != TOK_COLOURLIST) {
+ nErrors++;
+ pmprintf("Error, line %d: _colourlist expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ if (token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: colourlist name expected\n", nLines);
+ return -1;
+ }
+
+ for (ci = 0; ci < colorLists.size(); ci++) {
+ if (strcmp(colorLists.at(ci)->identity(), tokenStringVal) == 0)
+ break;
+ }
+ if (ci == colorLists.size()) {
+ nErrors++;
+ pmprintf("Error, line %d: no colourlist named %s exists\n",
+ nLines, tokenStringVal);
+ return -1;
+ }
+
+ QedColorList *colorList = colorLists.at(ci);
+ unsigned int nColours = colorList->length();
+ unsigned int nMetrics = mlp.length();
+
+ if (nColours != nMetrics) {
+ nErrors++;
+ pmprintf("Error, line %d: %d colours don't match %d metrics\n",
+ nLines, nColours, nMetrics);
+ return -1;
+ }
+
+ eofOK = eofMayFollow;
+ NextToken();
+
+ while (token == TOK_HORIZONTAL || token == TOK_VERTICAL) {
+ isVertical = (token == TOK_VERTICAL);
+ NextToken();
+ }
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedMultiBar* m = new QedMultiBar(parent, x, y, w, h, colorList, history);
+ if (m) {
+ if (isVertical)
+ m->setOrientation(Qt::Vertical);
+ if (max > 0.0)
+ m->setMaximum(max, clip);
+ m->setOutline(!noborder);
+ gadgets.append(m);
+ } else {
+ perror("no memory to allocate multibar gadget");
+ exit(1);
+ }
+
+ if (AddMetrics(update, mlp, m))
+ nWarnings++;
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ m->dump(stderr);
+ return 0;
+}
+
+static int
+ParseBarGraph(int eofMayFollow)
+{
+ int x, y, sts;
+ unsigned w, h;
+ MetricData* metricData;
+ double min = qQNaN(), max = qQNaN();
+ double update = appData.delta;
+ int fixMax = 0;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ sts = ParseUpdate(update, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token != TOK_METRIC) {
+ nErrors++;
+ pmprintf("Error, line %d: _metric expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ // There's only ever one metric for a bargraph, use chunkSize = 1
+ QList<MetricData*> mlp;
+ sts = ParseMetric(&metricData, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ mlp.append(metricData);
+
+ while (token == TOK_MIN || token == TOK_MAX || token == TOK_FIXED) {
+
+ eofOK = 0;
+ if (token == TOK_FIXED) {
+ NextToken();
+ if (token != TOK_MAX) {
+ nErrors++;
+ pmprintf("Error, line %d: _fixed _max expected\n", nLines);
+ return -1;
+ }
+ fixMax = 1;
+ }
+
+ if (token == TOK_MIN) {
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: minimum must be a number\n", nLines);
+ return -1;
+ }
+ min = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+ }
+ else {
+ NextToken();
+ if (token != TOK_INTEGER && token != TOK_REAL) {
+ nErrors++;
+ pmprintf("Error, line %d: maximum must be a number\n", nLines);
+ return -1;
+ }
+ max = (token == TOK_INTEGER) ? tokenIntVal : tokenRealVal;
+ }
+ eofOK = eofMayFollow;
+ NextToken();
+ }
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedBarGraph* b = new QedBarGraph(parent, x, y, w, h, w - 1);
+ if (b) {
+ if (min != qQNaN())
+ b->setMinimum(min);
+ if (max != qQNaN())
+ b->setMaximum(max);
+ if (fixMax)
+ b->clipRange();
+ gadgets.append(b);
+ } else {
+ perror("no memory for bargraph gadget");
+ exit(1);
+ }
+
+ if (AddMetrics(update, mlp, b))
+ nWarnings++;
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ b->dump(stderr);
+ return 0;
+}
+
+static int
+ParseLed(int eofMayFollow)
+{
+ int x, y, sts, li;
+ unsigned w, h;
+ MetricData* metricData;
+ double update = appData.delta;
+
+ eofOK = 0;
+ NextToken();
+
+ sts = ParseGeometry(x, y, w, h, eofMayFollow);
+ if (sts < 0)
+ return sts;
+
+ if (token == TOK_UPDATE) {
+ sts = ParseUpdate(update, 0);
+ if (sts < 0)
+ return sts;
+ }
+
+ if (token != TOK_METRIC) {
+ nErrors++;
+ pmprintf("Error, line %d: _metric expected\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ QList<MetricData*> mlp;
+ sts = ParseMetric(&metricData, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ mlp.append(metricData);
+
+ if (token != TOK_LEGEND) {
+ nErrors++;
+ pmprintf("Error, line %d: _legend expected for _led\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ if (token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: legend name expected\n", nLines);
+ return -1;
+ }
+
+ for (li = 0; li < legends.size(); li++) {
+ if (strcmp(legends.at(li)->identity(), tokenStringVal) == 0)
+ break;
+ }
+ if (li == colorLists.size()) {
+ nErrors++;
+ pmprintf("Error, line %d: no legend named %s exists\n",
+ nLines, tokenStringVal);
+ return -1;
+ }
+ QedLegend *legend = legends.at(li);
+
+ eofOK = eofMayFollow;
+ NextToken();
+
+ // Parse optional actions list
+ //
+ if (token == TOK_ACTIONLIST) {
+ sts = ParseActionList(AL_MAY_BE_REFERENCE, eofMayFollow);
+ if (sts < 0)
+ return sts;
+ }
+
+ QedRoundLED* l = new QedRoundLED(parent, x, y, w, h, legend);
+ if (l) {
+ gadgets.append(l);
+ } else {
+ perror("no memory for led gadget");
+ exit(1);
+ }
+
+ if (AddMetrics(update, mlp, l))
+ nWarnings++;
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ l->dump(stderr);
+ return 0;
+}
+
+static int
+ParseLegend(int eofMayFollow)
+{
+ eofOK = 0;
+
+ NextToken();
+ if (token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: name expected for legend\n", nLines);
+ return -1;
+ }
+ QedLegend* legend = new QedLegend(tokenStringVal);
+ if (legend == NULL) {
+ nErrors++;
+ fprintf(stderr,
+ "Error, line %d: out of memory allocating legend\n",
+ nLines);
+ return -1;
+ }
+ legends.append(legend);
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "legend:\n name \"%s\"\n", tokenStringVal);
+
+ NextToken();
+ if (token != TOK_LPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `(' expected for legend\n", nLines);
+ delete legend;
+ return -1;
+ }
+ NextToken();
+
+ while (token == TOK_INTEGER ||
+ token == TOK_REAL ||
+ token == TOK_DEFAULT) {
+ double val = 0.0;
+ int prevToken;
+ if (token != TOK_DEFAULT)
+ val = (token == TOK_REAL) ? tokenRealVal : tokenIntVal;
+ prevToken = token;
+ NextToken();
+ if (token != TOK_IDENTIFIER && token != TOK_STRING) {
+ nErrors++;
+ pmprintf("Error, line %d: colour name expected in legend\n", nLines);
+ delete legend;
+ return -1;
+ }
+ if (prevToken != TOK_DEFAULT)
+ legend->addThreshold(val, tokenStringVal);
+ else
+ legend->setDefaultColor(tokenStringVal);
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, " %.2f = %s\n", val, tokenStringVal);
+ NextToken();
+ }
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ putc('\n', stderr);
+
+ if (token != TOK_RPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `)' or threshold expected for legend\n",
+ nLines);
+ delete legend;
+ return -1;
+ }
+
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+static int
+ParseColorList(int eofMayFollow)
+{
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_IDENTIFIER) {
+ nErrors++;
+ pmprintf("Error, line %d: name expected for colourlist\n", nLines);
+ return -1;
+ }
+ char *name = tokenStringVal;
+ NextToken();
+
+ if (token != TOK_LPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `(' expected for colourlist\n", nLines);
+ return -1;
+ }
+ NextToken();
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, "colourlist: name \"%s\"\n", name);
+
+ QedColorList *colorList = new QedColorList(name);
+ if (colorList == NULL) {
+ nErrors++;
+ fprintf(stderr,
+ "Error, line %d: out of memory allocating colorList\n",
+ nLines);
+ return -1;
+ }
+ while (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ colorList->addColor(tokenStringVal);
+ if (pmDebug & DBG_TRACE_APPL0)
+ fprintf(stderr, " %s\n", tokenStringVal);
+ NextToken();
+ }
+
+ if (pmDebug & DBG_TRACE_APPL0)
+ putc('\n', stderr);
+
+ if (token != TOK_RPAREN) {
+ nErrors++;
+ pmprintf("Error, line %d: `)' expected for colourlist\n", nLines);
+ return -1;
+ }
+ colorLists.append(colorList);
+ eofOK = eofMayFollow;
+ NextToken();
+ return 0;
+}
+
+// Config files are typically created by programs rather than specified by the
+// user. In the config file header, after the version number, such programs
+// should put their argv so that the desktop can restart pmgadgets
+// (indirectly) by re-running the program that created the config file.
+//
+QStringList configArgv;
+
+void ParseVersion()
+{
+ if ((token == TOK_IDENTIFIER || token == TOK_STRING) &&
+ strcmp(tokenStringVal, "pmgadgets") == 0) {
+ eofOK = 0;
+ NextToken();
+ if (token != TOK_INTEGER) {
+ pmprintf("Error, line %d: bad pmgadgets config file version\n",
+ nLines);
+ pmflush();
+ exit(1);
+ }
+ if (tokenIntVal > 1) {
+ pmprintf("Error, line %d: you need a newer version pmgadgets to run this\n",
+ nLines);
+ pmflush();
+ exit(1);
+ }
+ else if (tokenIntVal < 1) {
+ pmprintf("Error, line %d: ludicrous pmgadgets config file version\n",
+ nLines);
+ pmflush();
+ exit(1);
+ }
+ eofOK = 1;
+ NextToken();
+ while (token == TOK_IDENTIFIER || token == TOK_STRING) {
+ configArgv.append(strdup(tokenStringVal));
+ NextToken();
+ }
+ }
+}
+
+int
+Parse(void)
+{
+ int sts;
+ NextToken();
+ setjmp(scannerEofEnv); // come back here on unexpected EOF
+
+ ParseVersion();
+ while (token != TOK_EOF) {
+ eofOK = 0;
+ switch (token) {
+ case TOK_LINE:
+ sts = ParseLine(1);
+ break;
+
+ case TOK_LABEL:
+ sts = ParseLabel(1);
+ break;
+
+ case TOK_BAR:
+ sts = ParseBar(1);
+ break;
+
+ case TOK_MULTIBAR:
+ sts = ParseMultibar(1);
+ break;
+
+ case TOK_BARGRAPH:
+ sts = ParseBarGraph(1);
+ break;
+
+ case TOK_LED:
+ sts = ParseLed(1);
+ break;
+
+ case TOK_LEGEND:
+ sts = ParseLegend(1);
+ break;
+
+ case TOK_COLOURLIST:
+ sts = ParseColorList(1);
+ break;
+
+ case TOK_ACTIONLIST:
+ sts = ParseActionList(AL_NEED_NAME, 1);
+ break;
+
+ default:
+ sts = -1;
+ nErrors++;
+ pmprintf("Error, line %u: Bad gadget type\n", nLines);
+ break;
+ }
+ if (sts == -1) {
+ eofOK = 1;
+ FindNewStatement();
+ }
+ const unsigned maxErrors = 10;
+ if (nErrors > maxErrors) {
+ pmprintf("Too many errors, giving up!\n");
+ break;
+ }
+ }
+ if (nErrors) {
+ pmprintf("%s: configuration file has errors "
+ "(%d lines parsed, %d errors)\n", pmProgname, nLines, nErrors);
+ pmflush();
+ return -1;
+ }
+ if (nWarnings)
+ pmflush();
+ return 0;
+}