301 lines
9.6 KiB
C
301 lines
9.6 KiB
C
/* Copyright (C) 2006 Sergei Golubchik
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2
|
|
as published by the Free Software Foundation
|
|
|
|
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
/*
|
|
originally based on:
|
|
WMgMon - Window Maker Generic Monitor
|
|
by Nicolas Chauvat <nico@caesium.fr>
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include "expr.h"
|
|
#include "panes.h"
|
|
|
|
#define _(S) S,sizeof(S)-1
|
|
struct {
|
|
char *name;
|
|
int name_length;
|
|
int default_height;
|
|
int possible_flags;
|
|
} pane_defs[]={
|
|
{ _("bar"), 1, P_LABEL | P_SMOOTH },
|
|
{ _("number"), 1, P_LABEL | P_FLOAT | P_SMOOTH },
|
|
{ _("percent"), 1, P_LABEL | P_SMOOTH },
|
|
{ _("graph"), 3, P_LABEL | P_SIZE | P_SMOOTH | P_SCALEDOWN | P_LOG },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
struct {
|
|
char *name;
|
|
int name_length;
|
|
int flag;
|
|
} options[]= {
|
|
{ _("-big"), P_BIG },
|
|
{ _("-float"), P_FLOAT },
|
|
{ _("-label"), P_LABEL },
|
|
{ _("-log"), P_LOG },
|
|
{ _("-medium"), P_MEDIUM },
|
|
{ _("-scaledown"), P_SCALEDOWN },
|
|
{ _("-small"), P_SMALL },
|
|
{ _("-smooth"), P_SMOOTH },
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
#undef _
|
|
|
|
static const char *parse_regex(regex_t *preg, char *re, unsigned *flags);
|
|
|
|
/******************************************************************************/
|
|
/* panes functions */
|
|
/******************************************************************************/
|
|
|
|
#define streq(A,B) !strcasecmp((A),(B))
|
|
#define error(format, ...) \
|
|
do { \
|
|
fprintf(stderr, "Line %d: " format "\n", line, ## __VA_ARGS__); \
|
|
return -1; \
|
|
} while (0)
|
|
#define dup_keyword error("Duplicate keyword '%s'", keyword)
|
|
|
|
static char *next_token(char *s)
|
|
{
|
|
char *p;
|
|
while (*s && !isspace(*s)) s++; p=s;
|
|
while (*s && isspace(*s)) s++; *p=0;
|
|
return s;
|
|
}
|
|
|
|
int read_config_file(pane_desc panes[], int *pane_num, const int max_pane,
|
|
stat_dev stats[], int *stat_num, const int stat_max,
|
|
wind_desc winds[], int *wind_num, const int wind_max)
|
|
{
|
|
int i, cur_wind=-1, cur_stat=-1, cur_pane=0, cur_part=-1, line=0;
|
|
char *home, buf[4096];
|
|
FILE *cfg;
|
|
|
|
if (!config) {
|
|
config=buf;
|
|
if (!(home=getenv("HOME"))) return 1;
|
|
snprintf(buf, sizeof(buf), "%s/.wmsupermonrc", home);
|
|
}
|
|
cfg=fopen(config, "r");
|
|
if(!cfg) return 1;
|
|
|
|
while (fgets(buf, sizeof(buf), cfg)) {
|
|
line++;
|
|
if (buf[0] == '#') {
|
|
/* comment */
|
|
} else if (buf[0] == '[' && buf[1] == '[') {
|
|
char *s=buf+2;
|
|
while (s[0] && s[0] != '\n' && !(s[0] == ']' && s[1] == ']')) s++;
|
|
if (s[0] != ']' || s[1] != ']') error("Syntax error");
|
|
if (s-buf > WNAME_LEN+2)
|
|
error("Too long name %s", buf);
|
|
*s=0; s=buf+2;
|
|
if (++cur_wind >= wind_max)
|
|
error("Too many window definition, max is %i", wind_max);
|
|
strncpy(winds[cur_wind].name, *s ? s : "wmsupermon", WNAME_LEN);
|
|
winds[cur_wind].panes=panes+cur_pane;
|
|
cur_part=0;
|
|
} else if (buf[0] == '[') {
|
|
cur_part=-1;
|
|
if (cur_stat != -1) {
|
|
if (!stats[cur_stat].source)
|
|
error("Label [%s] has no source", stats[cur_stat].name);
|
|
}
|
|
if (++cur_stat >= stat_max)
|
|
error("Too many stat definition, max is %i", stat_max);
|
|
stats[cur_stat].scale=1;
|
|
for (i=1; buf[i] !=']' && i <= NAME_LEN && buf[i] && buf[i] != '\n'; i++)
|
|
stats[cur_stat].name[i-1]=buf[i];
|
|
if (buf[i] != ']')
|
|
error("Too long label %s", buf);
|
|
} else if (cur_stat >= 0 || cur_part >=0) {
|
|
char *s=buf, *keyword, *value;
|
|
if (*s == '\n') s=keyword=value=0;
|
|
else {
|
|
while (isspace(*s)) s++;
|
|
keyword=s;
|
|
while (isalnum(*s) || *s == '/' || *s == '-' || *s == '%') s++;
|
|
value=s;
|
|
while (isspace(*s)) s++;
|
|
if (*s++ != '=') error("Syntax error");
|
|
while (isspace(*s)) s++;
|
|
*value=0;
|
|
value=s;
|
|
s+=strlen(value)-1;
|
|
while (isspace(*s)) s--;
|
|
s[1]=0;
|
|
}
|
|
|
|
if (cur_part >=0) {
|
|
if (!s) {
|
|
if (panes[cur_pane][0].stat) { cur_pane++; winds[cur_wind].num_panes++; }
|
|
cur_part=0;
|
|
} else {
|
|
pane_part *widget=&panes[cur_pane][cur_part];
|
|
int possible_flags;
|
|
|
|
if (cur_pane >= max_pane)
|
|
error("Too many pane definition, max is %i", max_pane);
|
|
|
|
/* label */
|
|
for (i=0; i <= cur_stat; i++) {
|
|
if (streq(keyword, stats[i].name))
|
|
break;
|
|
}
|
|
if (i > cur_stat)
|
|
error("unknown label %s", keyword);
|
|
widget->stat=&stats[i];
|
|
stats[i].flags|=F_USED;
|
|
|
|
/* widget name */
|
|
s=next_token(value);
|
|
for (i=0; pane_defs[i].name; i++)
|
|
if (streq(value, pane_defs[i].name))
|
|
break;
|
|
if (!pane_defs[i].name)
|
|
error("unknown widget %s", value);
|
|
widget->type=i;
|
|
|
|
/* options */
|
|
possible_flags=pane_defs[widget->type].possible_flags;
|
|
for ( s=next_token(value=s); *value; s=next_token(value=s)) {
|
|
for (i=0; options[i].name; i++)
|
|
if (streq(value, options[i].name))
|
|
break;
|
|
if (!options[i].name)
|
|
error("Unknown option %s", value);
|
|
if (!(possible_flags & options[i].flag))
|
|
error("Widget %s does not support the option %s",
|
|
pane_defs[widget->type].name, value);
|
|
if (options[i].flag & P_SIZE && widget->flags & P_SIZE)
|
|
error("Duplicate size option (-small, -medium, -big)");
|
|
widget->flags|=options[i].flag;
|
|
}
|
|
|
|
if (widget->flags & P_SMALL)
|
|
widget->height=1;
|
|
else if (widget->flags & P_MEDIUM)
|
|
widget->height=2;
|
|
else if (widget->flags & P_BIG)
|
|
widget->height=4;
|
|
else
|
|
widget->height=pane_defs[widget->type].default_height;
|
|
|
|
if (widget->flags & P_SMOOTH && !widget->stat->smooth)
|
|
widget->stat->smooth=calloc(SMOOTH_SIZE-1, sizeof(double));
|
|
if (widget->type == PTGraph) {
|
|
history **x=&widget->stat->hist[widget->flags & P_HIST];
|
|
if (!*x) {
|
|
*x=calloc(1, sizeof(history));
|
|
(*x)->max=1;
|
|
}
|
|
}
|
|
|
|
cur_part++;
|
|
}
|
|
} else if (!s) continue;
|
|
else if (streq(keyword, "source")) {
|
|
if (stats[cur_stat].source) dup_keyword;
|
|
stats[cur_stat].source=strdup(value);
|
|
} else if (streq(keyword, "regex")) { const char *err;
|
|
if (stats[cur_stat].expr) dup_keyword;
|
|
err=parse_regex(&stats[cur_stat].regex, value, &stats[cur_stat].flags);
|
|
if (err)
|
|
error("Regex: %s\n", err);
|
|
stats[cur_stat].expr=yy_expr;
|
|
stats[cur_stat].diff_old=calloc(yy_ndiff, sizeof(double));
|
|
stats[cur_stat].diff_new=calloc(yy_ndiff, sizeof(double));
|
|
stats[cur_stat].sum_acc =calloc(yy_nsum, sizeof(double));
|
|
stats[cur_stat].nsum=yy_nsum;
|
|
} else if (streq(keyword, "action")) {
|
|
if (stats[cur_stat].action) dup_keyword;
|
|
stats[cur_stat].action=strdup(value);
|
|
} else if (streq(keyword, "interval")) {
|
|
stats[cur_stat].hist_update_interval=
|
|
stats[cur_stat].update_interval=atoi(value);
|
|
} else if (streq(keyword, "scale")) {
|
|
stats[cur_stat].scale=atof(value);
|
|
} else if (streq(keyword, "range")) {
|
|
if (sscanf(value, " %lf .. %lf", &stats[cur_stat].min, &stats[cur_stat].max) != 2)
|
|
error("Syntax error.\n");
|
|
if (!(stats[cur_stat].max-=stats[cur_stat].min))
|
|
error("Invalid range.\n");
|
|
} else error("Syntax error.\n");
|
|
} else if (*buf != '\n')
|
|
error("Syntax error.\n");
|
|
}
|
|
|
|
if (cur_wind < 0)
|
|
error("No window definitions.\n");
|
|
if (cur_pane < 0)
|
|
error("No pane definitions.\n");
|
|
if (cur_stat < 0)
|
|
error("No stat definitions.\n");
|
|
|
|
*pane_num=cur_pane;
|
|
*stat_num=cur_stat+1;
|
|
*wind_num=cur_wind+1;
|
|
return 0;
|
|
}
|
|
|
|
static char bracket(char c)
|
|
{
|
|
return c == '<' ? '>' :
|
|
c == '{' ? '}' :
|
|
c == '[' ? ']' :
|
|
c == '(' ? ')' : c;
|
|
}
|
|
|
|
static const char *parse_regex(regex_t *preg, char *re, unsigned *flags)
|
|
{
|
|
char bra, ket, *s, *subs;
|
|
int cflags=REG_EXTENDED, err;
|
|
static char error_buf[256];
|
|
|
|
bra=*re++;
|
|
ket=bracket(bra);
|
|
for (s=re; *s && *s != ket; s++);
|
|
if (*s != ket) return "Not terminated regex";
|
|
*s++=0;
|
|
if (bra != ket) {
|
|
bra=*s++;
|
|
if (!bra) return "Missing substitution";
|
|
ket=bracket(bra);
|
|
}
|
|
for (subs=s; *s && *s != ket; s++);
|
|
if (*s != ket) return s == subs ? "Missing substitution" : "Not terminated substitution";
|
|
for (*s++=0; *s; s++)
|
|
if (*s == 'i') cflags|=REG_ICASE; else
|
|
if (*s == 's') *flags|=F_SINGLE_LINE; else
|
|
if (*s == 'd') *flags|=F_DEBUG; else
|
|
return "Trailing characters";
|
|
err=regcomp(preg, re, cflags);
|
|
if (err) {
|
|
regerror(err, preg, error_buf, sizeof(error_buf));
|
|
regfree(preg);
|
|
return error_buf;
|
|
}
|
|
|
|
yy_str=subs; yy_nsum=yy_ndiff=0;
|
|
strcpy(error_buf, "Expression: ");
|
|
yy_err=error_buf+12;
|
|
if (yyparse()) return error_buf;
|
|
return 0;
|
|
}
|
|
|