/* 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 */ #include #include #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; }