add if_match object comparing strings, floats and ints
authorPhil Sutter <phil@nwl.cc>
Sat, 20 Dec 2008 01:31:00 +0000 (02:31 +0100)
committerPhil Sutter <phil@nwl.cc>
Sat, 20 Dec 2008 15:14:57 +0000 (16:14 +0100)
In general, argument types should match, but for combined long and
double usage the long is being converted to double before evaluation.

A few examples:
${if_match ${cpu} < 30}
${if_match "asdf" != "qwer"}
${if_match 0.5 < 0.50001}
${if_match 49.999 < 50}

ChangeLog
README
doc/conky.1
doc/variables.xml
src/Makefile.am
src/algebra.c [new file with mode: 0644]
src/algebra.h [new file with mode: 0644]
src/conky.c
src/text_object.h

index 8fcd074..7635951 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+2008-12-20
+       * Add if_match object comparing strings, floats and ints
+
 2008-12-18
        * Fix segfault for diskiograph*, note the changed order of arguments
        * Fix documentation for all graph objects
diff --git a/README b/README
index 4670534..8f8853b 100644 (file)
--- a/README
+++ b/README
@@ -965,20 +965,36 @@ conky(1)                                                        conky(1)
              $if_empty and the matching $endif
 
 
+       1mif_existing file (string)0m
+             if FILE exists, display everything between if_existing  and  the
+             matching  $endif.  The optional second paramater checks for FILE
+             containing the specified string and  prints  everything  between
+             $if_existing and the matching $endif.
+
+
        1mif_gw 22mif there is at least one default gateway, display everything be‐
              tween $if_gw and the matching $endif
 
 
-       1mif_running (process)0m
-             if  PROCESS  is  running, display everything $if_running and the
-             matching $endif
+       1mif_match expression0m
+             Evaluates the given boolean expression, printing everything  be‐
+             tween $if_match and the matching $endif depending on whether the
+             evaluation returns true or not.  Valid expressions consist of  a
+             left  side,  an  operator and a right side. Left and right sides
+             are being parsed for contained text objects  before  evaluation.
+             Recognised left and right side types are:
 
+             1mdouble22m: argument consists of only digits and a single dot.
+             1mlong22m: argument consists of only digits.
+             1mstring22m: argument is enclosed in quotation mark or the checks for
+             double and long failed before.
 
-       1mif_existing file (string)0m
-             if FILE exists, display everything between if_existing  and  the
-             matching  $endif.  The optional second paramater checks for FILE
-             containing the specified string and  prints  everything  between
-             $if_existing and the matching $endif.
+             Valid operands are: ’>’, ’<’, ’>=’, ’<=’, ’==’, ’!=’.
+
+
+       1mif_running (process)0m
+             if PROCESS is running, display everything  $if_running  and  the
+             matching $endif
 
 
        1mif_mounted (mountpoint)0m
index 6e1aac7..9703a20 100644 (file)
@@ -823,27 +823,62 @@ laptops's LCD (0-7).
 
 .TP 
 \fB\*(T<\fBif_empty\fR\*(T>\fR \*(T<\fB(var)\fR\*(T> 
-if conky variable VAR is empty, display everything between $if_empty and the matching $endif
+if conky variable VAR is empty, display everything
+between $if_empty and the matching $endif
 
 .TP 
-\fB\*(T<\fBif_gw\fR\*(T>\fR 
-if there is at least one default gateway, display everything between $if_gw and the matching $endif
+\fB\*(T<\fBif_existing\fR\*(T>\fR \*(T<\fBfile (string)\fR\*(T> 
+if FILE exists, display everything between if_existing
+and the matching $endif. The optional second paramater
+checks for FILE containing the specified string and
+prints everything between $if_existing and the matching
+$endif.
 
 .TP 
-\fB\*(T<\fBif_running\fR\*(T>\fR \*(T<\fB(process)\fR\*(T> 
-if PROCESS is running, display everything $if_running and the matching $endif
+\fB\*(T<\fBif_gw\fR\*(T>\fR 
+if there is at least one default gateway, display
+everything between $if_gw and the matching $endif
+
+.TP 
+\fB\*(T<\fBif_match\fR\*(T>\fR \*(T<\fBexpression\fR\*(T> 
+Evaluates the given boolean expression, printing
+everything between $if_match and the matching $endif
+depending on whether the evaluation returns true or not.
+Valid expressions consist of a left side, an operator
+and a right side. Left and right sides are being parsed
+for contained text objects before evaluation. Recognised
+left and right side types are:
+
+\fBdouble\fR:
+argument consists of only digits and a
+single dot.
+.br
+\fBlong\fR:
+argument consists of only digits.
+.br
+\fBstring\fR:
+argument is enclosed in quotation mark
+or the checks for double and long failed
+before.
+
+Valid operands are:
+\&'>', '<', '>=', '<=', '==', '!='.
 
 .TP 
-\fB\*(T<\fBif_existing\fR\*(T>\fR \*(T<\fBfile (string)\fR\*(T> 
-if FILE exists, display everything between if_existing and the matching $endif. The optional second paramater checks for FILE containing the specified string and prints everything between $if_existing and the matching $endif.
+\fB\*(T<\fBif_running\fR\*(T>\fR \*(T<\fB(process)\fR\*(T> 
+if PROCESS is running, display everything $if_running
+and the matching $endif
 
 .TP 
 \fB\*(T<\fBif_mounted\fR\*(T>\fR \*(T<\fB(mountpoint)\fR\*(T> 
-if MOUNTPOINT is mounted, display everything between $if_mounted and the matching $endif
+if MOUNTPOINT is mounted, display everything between
+$if_mounted and the matching $endif
 
 .TP 
 \fB\*(T<\fBif_smapi_bat_installed\fR\*(T>\fR \*(T<\fB(INDEX)\fR\*(T> 
-when using smapi, if the battery with index INDEX is installed, display everything between $if_smapi_bat_installed and the matching $endif
+when using smapi, if the battery with index INDEX is
+installed, display everything between
+$if_smapi_bat_installed and the matching $endif
 
 .TP 
 \fB\*(T<\fBif_up\fR\*(T>\fR \*(T<\fB(interface)\fR\*(T> 
index c0b16af..d82456b 100644 (file)
                        Title of current tune with optional maximum length specifier
                        <para></para></listitem>
        </varlistentry>
-       
+
        <varlistentry>
                <term>
                        <command><option>audacious_main_volume</option></command>
                        <option>(var)</option>
                </term>
                <listitem>
-                       if conky variable VAR is empty, display everything between $if_empty and the matching $endif
+                       if conky variable VAR is empty, display everything
+                       between $if_empty and the matching $endif
+                       <para></para></listitem>
+       </varlistentry>
+
+       <varlistentry>
+               <term>
+                       <command><option>if_existing</option></command>
+                       <option>file (string)</option>
+               </term>
+               <listitem>
+                       if FILE exists, display everything between if_existing
+                       and the matching $endif.  The optional second paramater
+                       checks for FILE containing the specified string and
+                       prints everything between $if_existing and the matching
+                       $endif.
                        <para></para></listitem>
        </varlistentry>
 
                        <command><option>if_gw</option></command>
                </term>
                <listitem>
-                       if there is at least one default gateway, display everything between $if_gw and the matching $endif
+                       if there is at least one default gateway, display
+                       everything between $if_gw and the matching $endif
                        <para></para></listitem>
        </varlistentry>
 
        <varlistentry>
                <term>
-                       <command><option>if_running</option></command>
-                       <option>(process)</option>
+                       <command><option>if_match</option></command>
+                       <option>expression</option>
                </term>
                <listitem>
-                       if PROCESS is running, display everything $if_running and the matching $endif
+                       Evaluates the given boolean expression, printing
+                       everything between $if_match and the matching $endif
+                       depending on whether the evaluation returns true or not.
+                       Valid expressions consist of a left side, an operator
+                       and a right side. Left and right sides are being parsed
+                       for contained text objects before evaluation. Recognised
+                       left and right side types are:
+                       <simplelist>
+                               <member><command>double</command>:
+                                       argument consists of only digits and a
+                                       single dot.
+                               </member>
+                               <member><command>long</command>:
+                                       argument consists of only digits.
+                               </member>
+                               <member><command>string</command>:
+                                       argument is enclosed in quotation mark
+                                       or the checks for double and long failed
+                                       before.
+                               </member>
+                       </simplelist>
+                       Valid operands are:
+                       '&gt;', '&lt;', '&gt;=', '&lt;=', '==', '!='.
                        <para></para></listitem>
        </varlistentry>
 
        <varlistentry>
                <term>
-                       <command><option>if_existing</option></command>
-                       <option>file (string)</option>
+                       <command><option>if_running</option></command>
+                       <option>(process)</option>
                </term>
                <listitem>
-                       if FILE exists, display everything between if_existing and the matching $endif.  The optional second paramater checks for FILE containing the specified string and prints everything between $if_existing and the matching $endif.
+                       if PROCESS is running, display everything $if_running
+                       and the matching $endif
                        <para></para></listitem>
        </varlistentry>
 
                        <option>(mountpoint)</option>
                </term>
                <listitem>
-                       if MOUNTPOINT is mounted, display everything between $if_mounted and the matching $endif
+                       if MOUNTPOINT is mounted, display everything between
+                       $if_mounted and the matching $endif
                        <para></para></listitem>
        </varlistentry>
 
                        <option>(INDEX)</option>
                </term>
                <listitem>
-                       when using smapi, if the battery with index INDEX is installed, display everything between $if_smapi_bat_installed and the matching $endif
+                       when using smapi, if the battery with index INDEX is
+                       installed, display everything between
+                       $if_smapi_bat_installed and the matching $endif
                        <para></para></listitem>
        </varlistentry>
 
index 0b16dcb..d656e5d 100644 (file)
@@ -145,7 +145,9 @@ conky_SOURCES =             \
        temphelper.c            \
        temphelper.h            \
        text_object.h           \
-       text_object.c
+       text_object.c           \
+       algebra.h               \
+       algebra.c
 
 conky_LDFLAGS =                                \
        $(PTHREAD_LIBS)                         \
diff --git a/src/algebra.c b/src/algebra.c
new file mode 100644 (file)
index 0000000..56b889a
--- /dev/null
@@ -0,0 +1,239 @@
+/* Conky, a system monitor, based on torsmo
+ *
+ * Any original torsmo code is licensed under the BSD license
+ *
+ * All code written since the fork of torsmo is licensed under the GPL
+ *
+ * Please see COPYING for details
+ *
+ * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
+ * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
+ *     (see AUTHORS)
+ * 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 3 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#define _GNU_SOURCE
+#include "config.h"
+#include "algebra.h"
+#include "logging.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* find the operand in the given expression
+ * returns the index of the first op character or -1 on error
+ */
+int find_match_op(const char *expr)
+{
+       unsigned int idx;
+
+       for (idx = 0; idx < strlen(expr); idx++) {
+               switch (expr[idx]) {
+                       case '=':
+                       case '!':
+                               if (expr[idx + 1] != '=')
+                                       return -1;
+                               /* fall through */
+                       case '<':
+                       case '>':
+                               return idx;
+                               break;
+               }
+       }
+       return -1;
+}
+
+int get_match_type(const char *expr)
+{
+       int idx;
+       const char *str;
+
+       if ((idx = find_match_op(expr)) == -1)
+               return -1;
+       str = expr + idx;
+
+       if (*str == '=' && *(str + 1) == '=')
+               return OP_EQ;
+       else if (*str == '!' && *(str + 1) == '=')
+               return OP_NEQ;
+       else if (*str == '>') {
+               if (*(str + 1) == '=')
+                       return OP_GEQ;
+               return OP_GT;
+       } else if (*str == '<') {
+               if (*(str + 1) == '=')
+                       return OP_LEQ;
+               return OP_LT;
+       }
+       return -1;
+}
+
+
+
+/* generic compare function
+ *
+ * v is actually the difference of the compared values. For strings
+ * this is equal to the output of str(n)cmp(). Use a macro here, as
+ * it's type-independent.
+ */
+#define COMPARE(v, t) \
+       switch (t) { \
+               case OP_GT:  return (v > 0); \
+               case OP_LT:  return (v < 0); \
+               case OP_EQ:  return (v == 0); \
+               case OP_GEQ: return (v >= 0); \
+               case OP_LEQ: return (v <= 0); \
+               case OP_NEQ: return (v != 0); \
+       } \
+       return 0
+
+int lcompare(long a, enum match_type mtype, long b)
+{
+       DBGP2("comparing longs '%ld' and '%ld'", a, b);
+       COMPARE((a - b), mtype);
+}
+int dcompare(double a, enum match_type mtype, double b)
+{
+       DBGP2("comparing doubles '%.lf' and '%.lf'", a, b);
+       COMPARE((a - b), mtype);
+}
+
+int scompare(const char *a, enum match_type mtype, const char *b)
+{
+       DBGP2("comparing strings '%s' and '%s'", a, b);
+       COMPARE(strcmp(a, b), mtype);
+}
+
+enum arg_type get_arg_type(const char *arg)
+{
+       const char *p, *e;
+
+       p = arg;
+       e = arg + strlen(arg);
+
+       if (*(e - 1) == ' ')
+               e--;
+       while (*e && *e == ' ')
+               e--;
+       while (p != e && *p == ' ')
+               p++;
+
+       if (*p == '"' && *e == '"')
+               return ARG_STRING;
+
+       while (p != e) {
+               if (!isdigit(*p))
+                       break;
+               p++;
+       }
+       if (p == e)
+               return ARG_LONG;
+       if (*p == '.') {
+               p++;
+               while (p != e) {
+                       if (!isdigit(*p))
+                               return ARG_STRING;
+                       p++;
+               }
+               return ARG_DOUBLE;
+       }
+       return ARG_STRING;
+}
+
+char *arg_to_string(const char *arg)
+{
+       const char *start;
+       int len;
+
+       start = arg;
+       len = 0;
+       while (*start && *start == ' ')
+                       start++;
+       if (!(*(start++) == '"'))
+               return NULL;
+       while (start[len] != '"')
+               len++;
+       return strndup(start, len);
+}
+double arg_to_double(const char *arg)
+{
+       double d;
+       if (sscanf(arg, "%lf", &d) != 1) {
+               ERR("converting '%s' to double failed", arg);
+               return 0.0;
+       }
+       return d;
+}
+long arg_to_long(const char *arg)
+{
+       long l;
+       if (sscanf(arg, "%ld", &l) != 1) {
+               ERR("converting '%s' to long failed", arg);
+               return 0;
+       }
+       return l;
+}
+int compare(const char *expr)
+{
+       char *expr_dup;
+       int idx, mtype;
+       enum arg_type type1, type2;
+
+       idx = find_match_op(expr);
+       mtype = get_match_type(expr);
+
+       if (!idx || mtype == -1) {
+               ERR("failed to parse compare string '%s'", expr);
+               return -2;
+       }
+
+       expr_dup = strdup(expr);
+       expr_dup[idx] = '\0';
+       if (expr_dup[idx + 1] == '=')
+               expr_dup[++idx] = '\0';
+
+       type1 = get_arg_type(expr_dup);
+       type2 = get_arg_type(expr_dup + idx + 1);
+       if (type1 == ARG_LONG && type2 == ARG_DOUBLE)
+               type1 = ARG_DOUBLE;
+       if (type1 == ARG_DOUBLE && type2 == ARG_LONG)
+               type2 = ARG_DOUBLE;
+       if (type1 != type2) {
+               ERR("trying to compare args '%s' and '%s' of different type",
+                               expr_dup, (expr_dup + idx + 1));
+               return -2;
+       }
+       switch (type1) {
+               case ARG_STRING:
+                       {
+                               char *a, *b;
+                               a = arg_to_string(expr_dup);
+                               b = arg_to_string(expr_dup + idx + 1);
+                               idx = scompare(a, mtype, b);
+                               free(a);
+                               free(b);
+                               return idx;
+                       }
+               case ARG_LONG:
+                       return lcompare(arg_to_long(expr_dup), mtype,
+                                       arg_to_long(expr_dup + idx + 1));
+               case ARG_DOUBLE:
+                       return dcompare(arg_to_double(expr_dup), mtype,
+                                       arg_to_double(expr_dup + idx + 1));
+       }
+       /* not reached */
+       return -2;
+}
diff --git a/src/algebra.h b/src/algebra.h
new file mode 100644 (file)
index 0000000..3b26b2d
--- /dev/null
@@ -0,0 +1,47 @@
+/* Conky, a system monitor, based on torsmo
+ *
+ * Any original torsmo code is licensed under the BSD license
+ *
+ * All code written since the fork of torsmo is licensed under the GPL
+ *
+ * Please see COPYING for details
+ *
+ * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
+ * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
+ *     (see AUTHORS)
+ * 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 3 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef _ALGEBRA_H
+#define _ALGEBRA_H
+
+enum match_type {
+       OP_LT = 1,      /* < */
+       OP_GT = 2,      /* > */
+       OP_EQ = 3,      /* == */
+       OP_LEQ = 4,     /* <= */
+       OP_GEQ = 5,     /* >= */
+       OP_NEQ = 6,     /* != */
+};
+
+enum arg_type {
+       ARG_STRING = 1, /* "asdf" */
+       ARG_LONG = 2,   /* 123456 */
+       ARG_DOUBLE = 3, /* 12.456 */
+};
+
+int compare(const char *);
+
+#endif /* _ALGEBRA_H */
index 2a9e4b6..852bf97 100644 (file)
@@ -59,6 +59,7 @@
 #include <getopt.h>
 
 /* local headers */
+#include "algebra.h"
 #include "build.h"
 #include "diskio.h"
 #include "fs.h"
@@ -1267,6 +1268,7 @@ static void free_text_objects(struct text_object *root)
                                }
                                break;
                        case OBJ_if_empty:
+                       case OBJ_if_match:
                        case OBJ_if_existing:
                        case OBJ_if_mounted:
                        case OBJ_if_running:
@@ -2517,6 +2519,14 @@ static struct text_object *construct_text_object(const char *s,
                        obj->data.ifblock.s = strndup(arg, text_buffer_size);
                }
                obj_be_ifblock_if(obj);
+       END OBJ(if_match, 0)
+               if (!arg) {
+                       ERR("if_match needs arguments");
+                       obj->data.ifblock.s = 0;
+               } else {
+                       obj->data.ifblock.s = strndup(arg, text_buffer_size);
+               }
+               obj_be_ifblock_if(obj);
        END OBJ(if_existing, 0)
                if (!arg) {
                        ERR("if_existing needs an argument or two");
@@ -4572,6 +4582,28 @@ static void generate_text_internal(char *p, int p_max_size,
                                free_text_objects(&subroot);
                                free(tmp_info);
                        }
+                       OBJ(if_match) {
+                               char expression[max_user_text];
+                               int val;
+                               struct text_object subroot;
+                               struct information *tmp_info;
+
+                               tmp_info = malloc(sizeof(struct information));
+                               memcpy(tmp_info, cur, sizeof(struct information));
+                               parse_conky_vars(&subroot, obj->data.ifblock.s,
+                                               expression, tmp_info);
+                               DBGP("parsed arg into '%s'", expression);
+
+                               val = compare(expression);
+                               if (val == -2) {
+                                       ERR("compare failed for expression '%s'",
+                                                       expression);
+                               } else if (!val) {
+                                       DO_JUMP;
+                               }
+                               free_text_objects(&subroot);
+                               free(tmp_info);
+                       }
                        OBJ(if_existing) {
                                if (obj->data.ifblock.str
                                    && !check_contains(obj->data.ifblock.s,
index fb22ad3..49f5efb 100644 (file)
@@ -154,6 +154,7 @@ enum text_object_type {
        OBJ_wireless_link_bar,
 #endif /* __linux__ */
        OBJ_if_empty,
+       OBJ_if_match,
        OBJ_if_existing,
        OBJ_if_mounted,
        OBJ_if_running,