78654f5746d34af90fcaab61e502dbef640a80b5
[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-2008 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 "algebra.h"
30 #include "logging.h"
31 #include <ctype.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 /* find the operand in the given expression
37  * returns the index of the first op character or -1 on error
38  */
39 int find_match_op(const char *expr)
40 {
41         unsigned int idx;
42
43         for (idx = 0; idx < strlen(expr); idx++) {
44                 switch (expr[idx]) {
45                         case '=':
46                         case '!':
47                                 if (expr[idx + 1] != '=')
48                                         return -1;
49                                 /* fall through */
50                         case '<':
51                         case '>':
52                                 return idx;
53                                 break;
54                 }
55         }
56         return -1;
57 }
58
59 int get_match_type(const char *expr)
60 {
61         int idx;
62         const char *str;
63
64         if ((idx = find_match_op(expr)) == -1)
65                 return -1;
66         str = expr + idx;
67
68         if (*str == '=' && *(str + 1) == '=')
69                 return OP_EQ;
70         else if (*str == '!' && *(str + 1) == '=')
71                 return OP_NEQ;
72         else if (*str == '>') {
73                 if (*(str + 1) == '=')
74                         return OP_GEQ;
75                 return OP_GT;
76         } else if (*str == '<') {
77                 if (*(str + 1) == '=')
78                         return OP_LEQ;
79                 return OP_LT;
80         }
81         return -1;
82 }
83
84
85
86 /* generic compare function
87  *
88  * v is actually the difference of the compared values. For strings
89  * this is equal to the output of str(n)cmp(). Use a macro here, as
90  * it's type-independent.
91  */
92 #define COMPARE(v, t) \
93         switch (t) { \
94                 case OP_GT:  return (v > 0); \
95                 case OP_LT:  return (v < 0); \
96                 case OP_EQ:  return (v == 0); \
97                 case OP_GEQ: return (v >= 0); \
98                 case OP_LEQ: return (v <= 0); \
99                 case OP_NEQ: return (v != 0); \
100         } \
101         return 0
102
103 int lcompare(long a, enum match_type mtype, long b)
104 {
105         DBGP2("comparing longs '%ld' and '%ld'", a, b);
106         COMPARE((a - b), mtype);
107 }
108 int dcompare(double a, enum match_type mtype, double b)
109 {
110         DBGP2("comparing doubles '%.lf' and '%.lf'", a, b);
111         COMPARE((a - b), mtype);
112 }
113
114 int scompare(const char *a, enum match_type mtype, const char *b)
115 {
116         DBGP2("comparing strings '%s' and '%s'", a, b);
117         COMPARE(strcmp(a, b), mtype);
118 }
119
120 enum arg_type get_arg_type(const char *arg)
121 {
122         const char *p, *e;
123
124         p = arg;
125         e = arg + strlen(arg);
126
127         if (*(e - 1) == ' ')
128                 e--;
129         while (*e && *e == ' ')
130                 e--;
131         while (p != e && *p == ' ')
132                 p++;
133
134         if (*p == '"' && *e == '"')
135                 return ARG_STRING;
136
137         if (*p == '-')  //allow negative values
138                 p++;
139         while (p != e) {
140                 if (!isdigit(*p))
141                         break;
142                 p++;
143         }
144         if (p == e)
145                 return ARG_LONG;
146         if (*p == '.') {
147                 p++;
148                 while (p != e) {
149                         if (!isdigit(*p))
150                                 return ARG_STRING;
151                         p++;
152                 }
153                 return ARG_DOUBLE;
154         }
155         return ARG_STRING;
156 }
157
158 char *arg_to_string(const char *arg)
159 {
160         const char *start;
161         int len;
162
163         start = arg;
164         len = 0;
165         while (*start && *start == ' ')
166                         start++;
167         if (!(*(start++) == '"'))
168                 return NULL;
169         while (start[len] != '"')
170                 len++;
171         return strndup(start, len);
172 }
173 double arg_to_double(const char *arg)
174 {
175         double d;
176         if (sscanf(arg, "%lf", &d) != 1) {
177                 ERR("converting '%s' to double failed", arg);
178                 return 0.0;
179         }
180         return d;
181 }
182 long arg_to_long(const char *arg)
183 {
184         long l;
185         if (sscanf(arg, "%ld", &l) != 1) {
186                 ERR("converting '%s' to long failed", arg);
187                 return 0;
188         }
189         return l;
190 }
191 int compare(const char *expr)
192 {
193         char *expr_dup;
194         int idx, mtype;
195         enum arg_type type1, type2;
196
197         idx = find_match_op(expr);
198         mtype = get_match_type(expr);
199
200         if (!idx || mtype == -1) {
201                 ERR("failed to parse compare string '%s'", expr);
202                 return -2;
203         }
204
205         expr_dup = strdup(expr);
206         expr_dup[idx] = '\0';
207         if (expr_dup[idx + 1] == '=')
208                 expr_dup[++idx] = '\0';
209
210         type1 = get_arg_type(expr_dup);
211         type2 = get_arg_type(expr_dup + idx + 1);
212         if (type1 == ARG_LONG && type2 == ARG_DOUBLE)
213                 type1 = ARG_DOUBLE;
214         if (type1 == ARG_DOUBLE && type2 == ARG_LONG)
215                 type2 = ARG_DOUBLE;
216         if (type1 != type2) {
217                 ERR("trying to compare args '%s' and '%s' of different type",
218                                 expr_dup, (expr_dup + idx + 1));
219                 return -2;
220         }
221         switch (type1) {
222                 case ARG_STRING:
223                         {
224                                 char *a, *b;
225                                 a = arg_to_string(expr_dup);
226                                 b = arg_to_string(expr_dup + idx + 1);
227                                 idx = scompare(a, mtype, b);
228                                 free(a);
229                                 free(b);
230                                 return idx;
231                         }
232                 case ARG_LONG:
233                         return lcompare(arg_to_long(expr_dup), mtype,
234                                         arg_to_long(expr_dup + idx + 1));
235                 case ARG_DOUBLE:
236                         return dcompare(arg_to_double(expr_dup), mtype,
237                                         arg_to_double(expr_dup + idx + 1));
238         }
239         /* not reached */
240         return -2;
241 }