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