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