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