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