Fix removing the config and sending a SIGUSR1 results in segfault
[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)-1;
130
131         while (p != e && *e && *e == ' ')
132                 e--;
133         while (p != e && *p == ' ')
134                 p++;
135
136         if (*p == '"' && *e == '"')
137                 return ARG_STRING;
138
139         if (*p == '-')  //allow negative values
140                 p++;
141         while (p <= e) {
142                 if (!isdigit(*p))
143                         break;
144                 p++;
145         }
146         if (p == e+1)
147                 return ARG_LONG;
148         if (*p == '.') {
149                 p++;
150                 while (p <= e) {
151                         if (!isdigit(*p))
152                                 return ARG_BAD;
153                         p++;
154                 }
155                 return ARG_DOUBLE;
156         }
157         return ARG_BAD;
158 }
159
160 char *arg_to_string(const char *arg)
161 {
162         const char *start;
163         int len;
164
165         start = arg;
166         len = 0;
167         while (*start && *start == ' ')
168                         start++;
169         if (!(*(start++) == '"'))
170                 return NULL;
171         while (start[len] != '"')
172                 len++;
173         return strndup(start, len);
174 }
175 double arg_to_double(const char *arg)
176 {
177         double d;
178         if (sscanf(arg, "%lf", &d) != 1) {
179                 NORM_ERR("converting '%s' to double failed", arg);
180                 return 0.0;
181         }
182         return d;
183 }
184 long arg_to_long(const char *arg)
185 {
186         long l;
187         if (sscanf(arg, "%ld", &l) != 1) {
188                 NORM_ERR("converting '%s' to long failed", arg);
189                 return 0;
190         }
191         return l;
192 }
193 int compare(const char *expr)
194 {
195         char *expr_dup;
196         int idx, mtype;
197         enum arg_type type1, type2;
198         long lng_a, lng_b;
199         double dbl_a, dbl_b;
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_BAD || type2 == ARG_BAD) {
217                 NORM_ERR("Bad arguments: '%s' and '%s'", expr_dup, (expr_dup + idx + 1));
218                 free(expr_dup);
219                 return -2;
220         }
221         if (type1 == ARG_LONG && type2 == ARG_DOUBLE)
222                 type1 = ARG_DOUBLE;
223         if (type1 == ARG_DOUBLE && type2 == ARG_LONG)
224                 type2 = ARG_DOUBLE;
225         if (type1 != type2) {
226                 NORM_ERR("trying to compare args '%s' and '%s' of different type",
227                                 expr_dup, (expr_dup + idx + 1));
228                 free(expr_dup);
229                 return -2;
230         }
231         switch (type1) {
232                 case ARG_STRING:
233                         {
234                                 char *a, *b;
235                                 a = arg_to_string(expr_dup);
236                                 b = arg_to_string(expr_dup + idx + 1);
237                                 idx = scompare(a, mtype, b);
238                                 free(a);
239                                 free(b);
240                                 free(expr_dup);
241                                 return idx;
242                         }
243                 case ARG_LONG:
244                         lng_a = arg_to_long(expr_dup);
245                         lng_b = arg_to_long(expr_dup + idx + 1);
246                         free(expr_dup);
247                         return lcompare(lng_a, mtype, lng_b);
248                 case ARG_DOUBLE:
249                         dbl_a = arg_to_double(expr_dup);
250                         dbl_b = arg_to_double(expr_dup + idx + 1);
251                         free(expr_dup);
252                         return lcompare(dbl_a, mtype, dbl_b);
253                 case ARG_BAD: /* make_gcc_happy() */;
254         }
255         /* not reached */
256         free(expr_dup);
257         return -2;
258 }