6fda32d9e873a6e08251f115511f99233ea3a7cb
[monky] / src / algebra.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
11  *      (see AUTHORS)
12  * All rights reserved.
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * You should have received a copy of the GNU General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  *
26  */
27 #define _GNU_SOURCE
28 #include "config.h"
29 #include "conky.h"
30 #include "algebra.h"
31 #include "logging.h"
32 #include <ctype.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 /* find the operand in the given expression
38  * returns the index of the first op character or -1 on error
39  */
40 int find_match_op(const char *expr)
41 {
42         unsigned int idx;
43
44         for (idx = 0; idx < strlen(expr); idx++) {
45                 switch (expr[idx]) {
46                         case '=':
47                         case '!':
48                                 if (expr[idx + 1] != '=')
49                                         return -1;
50                                 /* fall through */
51                         case '<':
52                         case '>':
53                                 return idx;
54                                 break;
55                 }
56         }
57         return -1;
58 }
59
60 int get_match_type(const char *expr)
61 {
62         int idx;
63         const char *str;
64
65         if ((idx = find_match_op(expr)) == -1)
66                 return -1;
67         str = expr + idx;
68
69         if (*str == '=' && *(str + 1) == '=')
70                 return OP_EQ;
71         else if (*str == '!' && *(str + 1) == '=')
72                 return OP_NEQ;
73         else if (*str == '>') {
74                 if (*(str + 1) == '=')
75                         return OP_GEQ;
76                 return OP_GT;
77         } else if (*str == '<') {
78                 if (*(str + 1) == '=')
79                         return OP_LEQ;
80                 return OP_LT;
81         }
82         return -1;
83 }
84
85
86
87 /* generic compare function
88  *
89  * v is actually the difference of the compared values. For strings
90  * this is equal to the output of str(n)cmp(). Use a macro here, as
91  * it's type-independent.
92  */
93 #define COMPARE(v, t) \
94         switch (t) { \
95                 case OP_GT:  return (v > 0); \
96                 case OP_LT:  return (v < 0); \
97                 case OP_EQ:  return (v == 0); \
98                 case OP_GEQ: return (v >= 0); \
99                 case OP_LEQ: return (v <= 0); \
100                 case OP_NEQ: return (v != 0); \
101         } \
102         return 0
103
104 int lcompare(long a, enum match_type mtype, long b)
105 {
106         DBGP2("comparing longs '%ld' and '%ld'", a, b);
107         COMPARE((a - b), mtype);
108 }
109 int dcompare(double a, enum match_type mtype, double b)
110 {
111         DBGP2("comparing doubles '%.lf' and '%.lf'", a, b);
112         COMPARE((a - b), mtype);
113 }
114
115 int scompare(const char *a, enum match_type mtype, const char *b)
116 {
117         DBGP2("comparing strings '%s' and '%s'", a, b);
118         COMPARE(strcmp(a, b), mtype);
119 }
120
121 enum arg_type get_arg_type(const char *arg)
122 {
123         const char *p, *e;
124
125         p = arg;
126         e = arg + strlen(arg);
127
128         if (*(e - 1) == ' ')
129                 e--;
130         while (*e && *e == ' ')
131                 e--;
132         while (p != e && *p == ' ')
133                 p++;
134
135         if (*p == '"' && *e == '"')
136                 return ARG_STRING;
137
138         if (*p == '-')  //allow negative values
139                 p++;
140         while (p != e) {
141                 if (!isdigit(*p))
142                         break;
143                 p++;
144         }
145         if (p == e)
146                 return ARG_LONG;
147         if (*p == '.') {
148                 p++;
149                 while (p != e) {
150                         if (!isdigit(*p))
151                                 return ARG_STRING;
152                         p++;
153                 }
154                 return ARG_DOUBLE;
155         }
156         return ARG_STRING;
157 }
158
159 char *arg_to_string(const char *arg)
160 {
161         const char *start;
162         int len;
163
164         start = arg;
165         len = 0;
166         while (*start && *start == ' ')
167                         start++;
168         if (!(*(start++) == '"'))
169                 return NULL;
170         while (start[len] != '"')
171                 len++;
172         return strndup(start, len);
173 }
174 double arg_to_double(const char *arg)
175 {
176         double d;
177         if (sscanf(arg, "%lf", &d) != 1) {
178                 ERR("converting '%s' to double failed", arg);
179                 return 0.0;
180         }
181         return d;
182 }
183 long arg_to_long(const char *arg)
184 {
185         long l;
186         if (sscanf(arg, "%ld", &l) != 1) {
187                 ERR("converting '%s' to long failed", arg);
188                 return 0;
189         }
190         return l;
191 }
192 int compare(const char *expr)
193 {
194         char *expr_dup;
195         int idx, mtype;
196         enum arg_type type1, type2;
197
198         idx = find_match_op(expr);
199         mtype = get_match_type(expr);
200
201         if (!idx || mtype == -1) {
202                 ERR("failed to parse compare string '%s'", expr);
203                 return -2;
204         }
205
206         expr_dup = strdup(expr);
207         expr_dup[idx] = '\0';
208         if (expr_dup[idx + 1] == '=')
209                 expr_dup[++idx] = '\0';
210
211         type1 = get_arg_type(expr_dup);
212         type2 = get_arg_type(expr_dup + idx + 1);
213         if (type1 == ARG_LONG && type2 == ARG_DOUBLE)
214                 type1 = ARG_DOUBLE;
215         if (type1 == ARG_DOUBLE && type2 == ARG_LONG)
216                 type2 = ARG_DOUBLE;
217         if (type1 != type2) {
218                 ERR("trying to compare args '%s' and '%s' of different type",
219                                 expr_dup, (expr_dup + idx + 1));
220                 return -2;
221         }
222         switch (type1) {
223                 case ARG_STRING:
224                         {
225                                 char *a, *b;
226                                 a = arg_to_string(expr_dup);
227                                 b = arg_to_string(expr_dup + idx + 1);
228                                 idx = scompare(a, mtype, b);
229                                 free(a);
230                                 free(b);
231                                 return idx;
232                         }
233                 case ARG_LONG:
234                         return lcompare(arg_to_long(expr_dup), mtype,
235                                         arg_to_long(expr_dup + idx + 1));
236                 case ARG_DOUBLE:
237                         return dcompare(arg_to_double(expr_dup), mtype,
238                                         arg_to_double(expr_dup + idx + 1));
239         }
240         /* not reached */
241         return -2;
242 }