diff options
Diffstat (limited to 'src/pmgadgets/parse.cpp')
-rw-r--r-- | src/pmgadgets/parse.cpp | 1339 |
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; +} |