Merge pull request #683 from tmtom/barometer2
[collectd.git] / src / utils_match.c
1 /**
2  * collectd - src/utils_match.c
3  * Copyright (C) 2008       Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28 #include "common.h"
29 #include "plugin.h"
30
31 #include "utils_match.h"
32
33 #include <regex.h>
34
35 #define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
36 #define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
37
38 struct cu_match_s
39 {
40   regex_t regex;
41   regex_t excluderegex;
42   int flags;
43
44   int (*callback) (const char *str, char * const *matches, size_t matches_num,
45       void *user_data);
46   void *user_data;
47 };
48
49 /*
50  * Private functions
51  */
52 static char *match_substr (const char *str, int begin, int end)
53 {
54   char *ret;
55   size_t ret_len;
56
57   if ((begin < 0) || (end < 0) || (begin >= end))
58     return (NULL);
59   if ((size_t) end > (strlen (str) + 1))
60   {
61     ERROR ("utils_match: match_substr: `end' points after end of string.");
62     return (NULL);
63   }
64
65   ret_len = end - begin;
66   ret = (char *) malloc (sizeof (char) * (ret_len + 1));
67   if (ret == NULL)
68   {
69     ERROR ("utils_match: match_substr: malloc failed.");
70     return (NULL);
71   }
72
73   sstrncpy (ret, str + begin, ret_len + 1);
74   return (ret);
75 } /* char *match_substr */
76
77 static int default_callback (const char __attribute__((unused)) *str,
78     char * const *matches, size_t matches_num, void *user_data)
79 {
80   cu_match_value_t *data = (cu_match_value_t *) user_data;
81
82   if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
83   {
84     gauge_t value;
85     char *endptr = NULL;
86
87     if (matches_num < 2)
88       return (-1);
89
90     value = (gauge_t) strtod (matches[1], &endptr);
91     if (matches[1] == endptr)
92       return (-1);
93
94     if ((data->values_num == 0)
95         || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
96     {
97       data->value.gauge = value;
98     }
99     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
100     {
101       double f = ((double) data->values_num)
102         / ((double) (data->values_num + 1));
103       data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
104     }
105     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
106     {
107       if (data->value.gauge > value)
108         data->value.gauge = value;
109     }
110     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
111     {
112       if (data->value.gauge < value)
113         data->value.gauge = value;
114     }
115     else
116     {
117       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
118       return (-1);
119     }
120
121     data->values_num++;
122   }
123   else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
124   {
125     counter_t value;
126     char *endptr = NULL;
127
128     if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
129     {
130       data->value.counter++;
131       data->values_num++;
132       return (0);
133     }
134
135     if (matches_num < 2)
136       return (-1);
137
138     value = (counter_t) strtoull (matches[1], &endptr, 0);
139     if (matches[1] == endptr)
140       return (-1);
141
142     if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
143       data->value.counter = value;
144     else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
145       data->value.counter += value;
146     else
147     {
148       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
149       return (-1);
150     }
151
152     data->values_num++;
153   }
154   else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
155   {
156     derive_t value;
157     char *endptr = NULL;
158
159     if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
160     {
161       data->value.counter++;
162       data->values_num++;
163       return (0);
164     }
165
166     if (matches_num < 2)
167       return (-1);
168
169     value = (derive_t) strtoll (matches[1], &endptr, 0);
170     if (matches[1] == endptr)
171       return (-1);
172
173     if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
174       data->value.derive = value;
175     else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
176       data->value.derive += value;
177     else
178     {
179       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
180       return (-1);
181     }
182
183     data->values_num++;
184   }
185   else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
186   {
187     absolute_t value;
188     char *endptr = NULL;
189
190     if (matches_num < 2)
191       return (-1);
192
193     value = (absolute_t) strtoull (matches[1], &endptr, 0);
194     if (matches[1] == endptr)
195       return (-1);
196
197     if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
198       data->value.absolute = value;
199     else
200     {
201       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
202       return (-1);
203     }
204
205     data->values_num++;
206   }
207   else
208   {
209     ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
210     return (-1);
211   }
212
213   return (0);
214 } /* int default_callback */
215
216 /*
217  * Public functions
218  */
219 cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
220                 int (*callback) (const char *str,
221                   char * const *matches, size_t matches_num, void *user_data),
222                 void *user_data)
223 {
224   cu_match_t *obj;
225   int status;
226
227   DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
228          regex, excluderegex);
229
230   obj = (cu_match_t *) malloc (sizeof (cu_match_t));
231   if (obj == NULL)
232     return (NULL);
233   memset (obj, '\0', sizeof (cu_match_t));
234
235   status = regcomp (&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
236   if (status != 0)
237   {
238     ERROR ("Compiling the regular expression \"%s\" failed.", regex);
239     sfree (obj);
240     return (NULL);
241   }
242
243   if (excluderegex && strcmp(excluderegex, "") != 0) {
244     status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
245     if (status != 0)
246     {
247         ERROR ("Compiling the excluding regular expression \"%s\" failed.",
248                excluderegex);
249         sfree (obj);
250         return (NULL);
251     }
252     obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
253   }
254
255   obj->callback = callback;
256   obj->user_data = user_data;
257
258   return (obj);
259 } /* cu_match_t *match_create_callback */
260
261 cu_match_t *match_create_simple (const char *regex,
262                                  const char *excluderegex, int match_ds_type)
263 {
264   cu_match_value_t *user_data;
265   cu_match_t *obj;
266
267   user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
268   if (user_data == NULL)
269     return (NULL);
270   memset (user_data, '\0', sizeof (cu_match_value_t));
271   user_data->ds_type = match_ds_type;
272
273   obj = match_create_callback (regex, excluderegex,
274                                default_callback, user_data);
275   if (obj == NULL)
276   {
277     sfree (user_data);
278     return (NULL);
279   }
280
281   obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
282
283   return (obj);
284 } /* cu_match_t *match_create_simple */
285
286 void match_destroy (cu_match_t *obj)
287 {
288   if (obj == NULL)
289     return;
290
291   if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
292   {
293     sfree (obj->user_data);
294   }
295
296   sfree (obj);
297 } /* void match_destroy */
298
299 int match_apply (cu_match_t *obj, const char *str)
300 {
301   int status;
302   regmatch_t re_match[32];
303   char *matches[32];
304   size_t matches_num;
305   size_t i;
306
307   if ((obj == NULL) || (str == NULL))
308     return (-1);
309
310   if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
311     status = regexec (&obj->excluderegex, str,
312                       STATIC_ARRAY_SIZE (re_match), re_match,
313                       /* eflags = */ 0);
314     /* Regex did match, so exclude this line */
315     if (status == 0) {
316       DEBUG("ExludeRegex matched, don't count that line\n");
317       return (0);
318     }
319   }
320
321   status = regexec (&obj->regex, str,
322       STATIC_ARRAY_SIZE (re_match), re_match,
323       /* eflags = */ 0);
324
325   /* Regex did not match */
326   if (status != 0)
327     return (0);
328
329   memset (matches, '\0', sizeof (matches));
330   for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
331   {
332     if ((re_match[matches_num].rm_so < 0)
333         || (re_match[matches_num].rm_eo < 0))
334       break;
335
336     matches[matches_num] = match_substr (str,
337         re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
338     if (matches[matches_num] == NULL)
339     {
340       status = -1;
341       break;
342     }
343   }
344
345   if (status != 0)
346   {
347     ERROR ("utils_match: match_apply: match_substr failed.");
348   }
349   else
350   {
351     status = obj->callback (str, matches, matches_num, obj->user_data);
352     if (status != 0)
353     {
354       ERROR ("utils_match: match_apply: callback failed.");
355     }
356   }
357
358   for (i = 0; i < matches_num; i++)
359   {
360     sfree (matches[i]);
361   }
362
363   return (status);
364 } /* int match_apply */
365
366 void *match_get_user_data (cu_match_t *obj)
367 {
368   if (obj == NULL)
369     return (NULL);
370   return (obj->user_data);
371 } /* void *match_get_user_data */
372
373 /* vim: set sw=2 sts=2 ts=8 : */