Revert "Uhh..ansohus"
[monky] / src / core.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
32 /* local headers */
33 #include "core.h"
34 #include "text_object.h"
35 #include "algebra.h"
36 #include "build.h"
37 #include "colours.h"
38 #include "diskio.h"
39 #ifdef X11
40 #include "fonts.h"
41 #endif
42 #include "fs.h"
43 #include "logging.h"
44 #include "mixer.h"
45 #include "mail.h"
46 #include "mboxscan.h"
47 #include "specials.h"
48 #include "temphelper.h"
49 #include "tailhead.h"
50 #include "top.h"
51
52 /* check for OS and include appropriate headers */
53 #if defined(__linux__)
54 #include "linux.h"
55 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
56 #include "freebsd.h"
57 #elif defined(__OpenBSD__)
58 #include "openbsd.h"
59 #endif
60
61 #include <string.h>
62 #include <ctype.h>
63
64 #ifdef HAVE_ICONV
65 #include <iconv.h>
66
67 #define ICONV_CODEPAGE_LENGTH 20
68
69 int register_iconv(iconv_t *new_iconv);
70
71 long iconv_selected;
72 long iconv_count = 0;
73 char iconv_converting;
74 static iconv_t **iconv_cd = 0;
75
76 char is_iconv_converting(void)
77 {
78         return iconv_converting;
79 }
80
81 void set_iconv_converting(char i)
82 {
83         iconv_converting = i;
84 }
85
86 long get_iconv_selected(void)
87 {
88         return iconv_selected;
89 }
90
91 void set_iconv_selected(long i)
92 {
93         iconv_selected = i;
94 }
95
96 int register_iconv(iconv_t *new_iconv)
97 {
98         iconv_cd = realloc(iconv_cd, sizeof(iconv_t *) * (iconv_count + 1));
99         if (!iconv_cd) {
100                 CRIT_ERR(NULL, NULL, "Out of memory");
101         }
102         iconv_cd[iconv_count] = malloc(sizeof(iconv_t));
103         if (!iconv_cd[iconv_count]) {
104                 CRIT_ERR(NULL, NULL, "Out of memory");
105         }
106         memcpy(iconv_cd[iconv_count], new_iconv, sizeof(iconv_t));
107         iconv_count++;
108         return iconv_count;
109 }
110
111 void free_iconv(void)
112 {
113         if (iconv_cd) {
114                 long i;
115
116                 for (i = 0; i < iconv_count; i++) {
117                         if (iconv_cd[i]) {
118                                 iconv_close(*iconv_cd[i]);
119                                 free(iconv_cd[i]);
120                         }
121                 }
122                 free(iconv_cd);
123         }
124         iconv_cd = 0;
125 }
126
127 void iconv_convert(size_t a, char *buff_in, char *p, size_t p_max_size)
128 {
129         if (a > 0 && is_iconv_converting() && get_iconv_selected() > 0
130                         && (iconv_cd[iconv_selected - 1] != (iconv_t) (-1))) {
131                 int bytes;
132                 size_t dummy1, dummy2;
133 #ifdef __FreeBSD__
134                 const char *ptr = buff_in;
135 #else
136                 char *ptr = buff_in;
137 #endif
138                 char *outptr = p;
139
140                 dummy1 = dummy2 = a;
141
142                 strncpy(buff_in, p, p_max_size);
143
144                 iconv(*iconv_cd[iconv_selected - 1], NULL, NULL, NULL, NULL);
145                 while (dummy1 > 0) {
146                         bytes = iconv(*iconv_cd[iconv_selected - 1], &ptr, &dummy1,
147                                         &outptr, &dummy2);
148                         if (bytes == -1) {
149                                 NORM_ERR("Iconv codeset conversion failed");
150                                 break;
151                         }
152                 }
153
154                 /* It is nessecary when we are converting from multibyte to
155                  * singlebyte codepage */
156                 a = outptr - p;
157         }
158 }
159 #endif /* HAVE_ICONV */
160
161 /* strip a leading /dev/ if any, following symlinks first
162  *
163  * BEWARE: this function returns a pointer to static content
164  *         which gets overwritten in consecutive calls. I.e.:
165  *         this function is NOT reentrant.
166  */
167 static const char *dev_name(const char *path)
168 {
169         static char buf[255];   /* should be enough for pathnames */
170         ssize_t buflen;
171
172         if (!path)
173                 return NULL;
174
175 #define DEV_NAME(x) \
176   x != NULL && strlen(x) > 5 && strncmp(x, "/dev/", 5) == 0 ? x + 5 : x
177         if ((buflen = readlink(path, buf, 254)) == -1)
178                 return DEV_NAME(path);
179         buf[buflen] = '\0';
180         return DEV_NAME(buf);
181 #undef DEV_NAME
182 }
183
184 static struct text_object *new_text_object_internal(void)
185 {
186         struct text_object *obj = malloc(sizeof(struct text_object));
187         memset(obj, 0, sizeof(struct text_object));
188         return obj;
189 }
190
191 static struct text_object *create_plain_text(const char *s)
192 {
193         struct text_object *obj;
194
195         if (s == NULL || *s == '\0') {
196                 return NULL;
197         }
198
199         obj = new_text_object_internal();
200
201         obj->type = OBJ_text;
202         obj->data.s = strndup(s, text_buffer_size);
203         return obj;
204 }
205
206 /* construct_text_object() creates a new text_object */
207 struct text_object *construct_text_object(const char *s, const char *arg, long
208                 line, void **ifblock_opaque, void *free_at_crash)
209 {
210         // struct text_object *obj = new_text_object();
211         struct text_object *obj = new_text_object_internal();
212
213         obj->line = line;
214
215 #define OBJ(a, n) if (strcmp(s, #a) == 0) { \
216         obj->type = OBJ_##a; need_mask |= (1ULL << n); {
217 #define OBJ_IF(a, n) if (strcmp(s, #a) == 0) { \
218         obj->type = OBJ_##a; need_mask |= (1ULL << n); \
219         obj_be_ifblock_if(ifblock_opaque, obj); {
220 #define END } } else
221
222 #define SIZE_DEFAULTS(arg) { \
223         obj->a = default_##arg##_width; \
224         obj->b = default_##arg##_height; \
225 }
226
227 #ifdef X11
228         if (s[0] == '#') {
229                 obj->type = OBJ_color;
230                 obj->data.l = get_x11_color(s);
231         } else
232 #endif /* X11 */
233 #ifdef __OpenBSD__
234         OBJ(freq, INFO_FREQ)
235 #else
236         OBJ(acpitemp, 0)
237                 obj->data.i = open_acpi_temperature(arg);
238         END OBJ(acpiacadapter, 0)
239         END OBJ(freq, INFO_FREQ)
240 #endif /* !__OpenBSD__ */
241                 get_cpu_count();
242                 if (!arg || !isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) == 0
243                                 || (unsigned int) atoi(&arg[0]) > info.cpu_count) {
244                         obj->data.cpu_index = 1;
245                         /* NORM_ERR("freq: Invalid CPU number or you don't have that many CPUs! "
246                                 "Displaying the clock for CPU 1."); */
247                 } else {
248                         obj->data.cpu_index = atoi(&arg[0]);
249                 }
250                 obj->a = 1;
251         END OBJ(freq_g, INFO_FREQ)
252                 get_cpu_count();
253                 if (!arg || !isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) == 0
254                                 || (unsigned int) atoi(&arg[0]) > info.cpu_count) {
255                         obj->data.cpu_index = 1;
256                         /* NORM_ERR("freq_g: Invalid CPU number or you don't have that many "
257                                 "CPUs! Displaying the clock for CPU 1."); */
258                 } else {
259                         obj->data.cpu_index = atoi(&arg[0]);
260                 }
261                 obj->a = 1;
262         END OBJ(read_tcp, 0)
263                 if (arg) {
264                         obj->data.read_tcp.host = malloc(text_buffer_size);
265                         sscanf(arg, "%s", obj->data.read_tcp.host);
266                         sscanf(arg+strlen(obj->data.read_tcp.host), "%u", &(obj->data.read_tcp.port));
267                         if(obj->data.read_tcp.port == 0) {
268                                 obj->data.read_tcp.port = atoi(obj->data.read_tcp.host);
269                                 strcpy(obj->data.read_tcp.host,"localhost");
270                         }
271                         obj->data.read_tcp.port = htons(obj->data.read_tcp.port);
272                         if(obj->data.read_tcp.port < 1 || obj->data.read_tcp.port > 65535) {
273                                 CRIT_ERR(obj, free_at_crash, "read_tcp: Needs \"(host) port\" as argument(s)");
274                         }
275                 }else{
276                         CRIT_ERR(obj, free_at_crash, "read_tcp: Needs \"(host) port\" as argument(s)");
277                 }
278 #if defined(__linux__)
279         END OBJ(voltage_mv, 0)
280                 get_cpu_count();
281                 if (!arg || !isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) == 0
282                                 || (unsigned int) atoi(&arg[0]) > info.cpu_count) {
283                         obj->data.cpu_index = 1;
284                         /* NORM_ERR("voltage_mv: Invalid CPU number or you don't have that many "
285                                 "CPUs! Displaying voltage for CPU 1."); */
286                 } else {
287                         obj->data.cpu_index = atoi(&arg[0]);
288                 }
289                 obj->a = 1;
290         END OBJ(voltage_v, 0)
291                 get_cpu_count();
292                 if (!arg || !isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) == 0
293                                 || (unsigned int) atoi(&arg[0]) > info.cpu_count) {
294                         obj->data.cpu_index = 1;
295                         /* NORM_ERR("voltage_v: Invalid CPU number or you don't have that many "
296                                 "CPUs! Displaying voltage for CPU 1."); */
297                 } else {
298                         obj->data.cpu_index = atoi(&arg[0]);
299                 }
300                 obj->a = 1;
301
302 #ifdef HAVE_IWLIB
303         END OBJ(wireless_essid, INFO_NET)
304                 if (arg) {
305                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
306                 } else {
307                         // default to DEFAULTNETDEV
308                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
309                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
310                         free(buf);
311                 }
312         END OBJ(wireless_mode, INFO_NET)
313                 if (arg) {
314                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
315                 } else {
316                         // default to DEFAULTNETDEV
317                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
318                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
319                         free(buf);
320                 }
321         END OBJ(wireless_bitrate, INFO_NET)
322                 if (arg) {
323                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
324                 } else {
325                         // default to DEFAULTNETDEV
326                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
327                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
328                         free(buf);
329                 }
330         END OBJ(wireless_ap, INFO_NET)
331                 if (arg) {
332                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
333                 } else {
334                         // default to DEFAULTNETDEV
335                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
336                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
337                         free(buf);
338                 }
339         END OBJ(wireless_link_qual, INFO_NET)
340                 if (arg) {
341                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
342                 } else {
343                         // default to DEFAULTNETDEV
344                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
345                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
346                         free(buf);
347                 }
348         END OBJ(wireless_link_qual_max, INFO_NET)
349                 if (arg) {
350                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
351                 } else {
352                         // default to DEFAULTNETDEV
353                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
354                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
355                         free(buf);
356                 }
357         END OBJ(wireless_link_qual_perc, INFO_NET)
358                 if (arg) {
359                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
360                 } else {
361                         // default to DEFAULTNETDEV
362                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
363                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
364                         free(buf);
365                 }
366         END OBJ(wireless_link_bar, INFO_NET)
367                 SIZE_DEFAULTS(bar);
368                 if (arg) {
369                         arg = scan_bar(arg, &obj->a, &obj->b);
370                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
371                 } else {
372                         // default to DEFAULTNETDEV
373                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
374                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
375                         free(buf);
376                 }
377 #endif /* HAVE_IWLIB */
378
379 #endif /* __linux__ */
380
381 #ifndef __OpenBSD__
382         END OBJ(acpifan, 0)
383         END OBJ(battery, 0)
384                 char bat[64];
385
386                 if (arg) {
387                         sscanf(arg, "%63s", bat);
388                 } else {
389                         strcpy(bat, "BAT0");
390                 }
391                 obj->data.s = strndup(bat, text_buffer_size);
392         END OBJ(battery_short, 0)
393                 char bat[64];
394
395                 if (arg) {
396                         sscanf(arg, "%63s", bat);
397                 } else {
398                         strcpy(bat, "BAT0");
399                 }
400                 obj->data.s = strndup(bat, text_buffer_size);
401         END OBJ(battery_time, 0)
402                 char bat[64];
403
404                 if (arg) {
405                         sscanf(arg, "%63s", bat);
406                 } else {
407                         strcpy(bat, "BAT0");
408                 }
409                 obj->data.s = strndup(bat, text_buffer_size);
410         END OBJ(battery_percent, 0)
411                 char bat[64];
412
413                 if (arg) {
414                         sscanf(arg, "%63s", bat);
415                 } else {
416                         strcpy(bat, "BAT0");
417                 }
418                 obj->data.s = strndup(bat, text_buffer_size);
419         END OBJ(battery_bar, 0)
420                 char bat[64];
421                 SIZE_DEFAULTS(bar);
422                 obj->b = 6;
423                 if (arg) {
424                         arg = scan_bar(arg, &obj->a, &obj->b);
425                         sscanf(arg, "%63s", bat);
426                 } else {
427                         strcpy(bat, "BAT0");
428                 }
429                 obj->data.s = strndup(bat, text_buffer_size);
430 #endif /* !__OpenBSD__ */
431
432 #if defined(__linux__)
433         END OBJ(disk_protect, 0)
434                 if (arg)
435                         obj->data.s = strndup(dev_name(arg), text_buffer_size);
436                 else
437                         CRIT_ERR(obj, free_at_crash, "disk_protect needs an argument");
438         END OBJ(i8k_version, INFO_I8K)
439         END OBJ(i8k_bios, INFO_I8K)
440         END OBJ(i8k_serial, INFO_I8K)
441         END OBJ(i8k_cpu_temp, INFO_I8K)
442         END OBJ(i8k_left_fan_status, INFO_I8K)
443         END OBJ(i8k_right_fan_status, INFO_I8K)
444         END OBJ(i8k_left_fan_rpm, INFO_I8K)
445         END OBJ(i8k_right_fan_rpm, INFO_I8K)
446         END OBJ(i8k_ac_status, INFO_I8K)
447         END OBJ(i8k_buttons_status, INFO_I8K)
448 #if defined(IBM)
449         END OBJ(ibm_fan, 0)
450         END OBJ(ibm_temps, 0)
451                 if (!arg) {
452                         CRIT_ERR(obj, free_at_crash, "ibm_temps: needs an argument");
453                 }
454                 if (!isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) >= 8) {
455                         obj->data.sensor = 0;
456                         NORM_ERR("Invalid temperature sensor! Sensor number must be 0 to 7. "
457                                 "Using 0 (CPU temp sensor).");
458                 }
459                 obj->data.sensor = atoi(&arg[0]);
460         END OBJ(ibm_volume, 0)
461         END OBJ(ibm_brightness, 0)
462 #endif
463         /* information from sony_laptop kernel module
464          * /sys/devices/platform/sony-laptop */
465         END OBJ(sony_fanspeed, 0)
466         END OBJ_IF(if_gw, INFO_GW)
467         END OBJ(ioscheduler, 0)
468                 if (!arg) {
469                         CRIT_ERR(obj, free_at_crash, "get_ioscheduler needs an argument (e.g. hda)");
470                         obj->data.s = 0;
471                 } else
472                         obj->data.s = strndup(dev_name(arg), text_buffer_size);
473         END OBJ(laptop_mode, 0)
474         END OBJ(pb_battery, 0)
475                 if (arg && strcmp(arg, "status") == EQUAL) {
476                         obj->data.i = PB_BATT_STATUS;
477                 } else if (arg && strcmp(arg, "percent") == EQUAL) {
478                         obj->data.i = PB_BATT_PERCENT;
479                 } else if (arg && strcmp(arg, "time") == EQUAL) {
480                         obj->data.i = PB_BATT_TIME;
481                 } else {
482                         NORM_ERR("pb_battery: needs one argument: status, percent or time");
483                         free(obj);
484                         return NULL;
485                 }
486
487 #endif /* __linux__ */
488 #if (defined(__FreeBSD__) || defined(__linux__))
489         END OBJ_IF(if_up, 0)
490                 if (!arg) {
491                         NORM_ERR("if_up needs an argument");
492                         obj->data.ifblock.s = 0;
493                 } else {
494                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
495                 }
496 #endif
497 #if defined(__OpenBSD__)
498         END OBJ(obsd_sensors_temp, 0)
499                 if (!arg) {
500                         CRIT_ERR(obj, free_at_crash, "obsd_sensors_temp: needs an argument");
501                 }
502                 if (!isdigit(arg[0]) || atoi(&arg[0]) < 0
503                                 || atoi(&arg[0]) > OBSD_MAX_SENSORS - 1) {
504                         obj->data.sensor = 0;
505                         NORM_ERR("Invalid temperature sensor number!");
506                 }
507                 obj->data.sensor = atoi(&arg[0]);
508         END OBJ(obsd_sensors_fan, 0)
509                 if (!arg) {
510                         CRIT_ERR(obj, free_at_crash, "obsd_sensors_fan: needs 2 arguments (device and sensor "
511                                 "number)");
512                 }
513                 if (!isdigit(arg[0]) || atoi(&arg[0]) < 0
514                                 || atoi(&arg[0]) > OBSD_MAX_SENSORS - 1) {
515                         obj->data.sensor = 0;
516                         NORM_ERR("Invalid fan sensor number!");
517                 }
518                 obj->data.sensor = atoi(&arg[0]);
519         END OBJ(obsd_sensors_volt, 0)
520                 if (!arg) {
521                         CRIT_ERR(obj, free_at_crash, "obsd_sensors_volt: needs 2 arguments (device and sensor "
522                                 "number)");
523                 }
524                 if (!isdigit(arg[0]) || atoi(&arg[0]) < 0
525                                 || atoi(&arg[0]) > OBSD_MAX_SENSORS - 1) {
526                         obj->data.sensor = 0;
527                         NORM_ERR("Invalid voltage sensor number!");
528                 }
529                 obj->data.sensor = atoi(&arg[0]);
530         END OBJ(obsd_vendor, 0)
531         END OBJ(obsd_product, 0)
532 #endif /* __OpenBSD__ */
533         END OBJ(buffers, INFO_BUFFERS)
534         END OBJ(cached, INFO_BUFFERS)
535 #define SCAN_CPU(__arg, __var) { \
536         int __offset = 0; \
537         if (__arg && sscanf(__arg, " cpu%u %n", &__var, &__offset) > 0) \
538                 __arg += __offset; \
539         else \
540                 __var = 0; \
541 }
542         END OBJ(cpu, INFO_CPU)
543                 SCAN_CPU(arg, obj->data.cpu_index);
544                 DBGP2("Adding $cpu for CPU %d", obj->data.cpu_index);
545 #ifdef X11
546         END OBJ(cpugauge, INFO_CPU)
547                 SIZE_DEFAULTS(gauge);
548                 SCAN_CPU(arg, obj->data.cpu_index);
549                 scan_gauge(arg, &obj->a, &obj->b);
550                 DBGP2("Adding $cpugauge for CPU %d", obj->data.cpu_index);
551 #endif /* X11 */
552         END OBJ(cpubar, INFO_CPU)
553                 SIZE_DEFAULTS(bar);
554                 SCAN_CPU(arg, obj->data.cpu_index);
555                 scan_bar(arg, &obj->a, &obj->b);
556                 DBGP2("Adding $cpubar for CPU %d", obj->data.cpu_index);
557 #ifdef X11
558         END OBJ(cpugraph, INFO_CPU)
559                 char *buf = 0;
560                 SIZE_DEFAULTS(graph);
561                 SCAN_CPU(arg, obj->data.cpu_index);
562                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
563                         &obj->e, &obj->char_a, &obj->char_b);
564                 DBGP2("Adding $cpugraph for CPU %d", obj->data.cpu_index);
565                 if (buf) free(buf);
566         END OBJ(loadgraph, INFO_LOADAVG)
567                 char *buf = 0;
568                 SIZE_DEFAULTS(graph);
569                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
570                                 &obj->e, &obj->char_a, &obj->char_b);
571                 if (buf) {
572                         int a = 1, r = 3;
573                         if (arg) {
574                                 r = sscanf(arg, "%d", &a);
575                         }
576                         obj->data.loadavg[0] = (r >= 1) ? (unsigned char) a : 0;
577                         free(buf);
578                 }
579 #endif /* X11 */
580         END OBJ(diskio, INFO_DISKIO)
581                 obj->data.diskio = prepare_diskio_stat(dev_name(arg));
582         END OBJ(diskio_read, INFO_DISKIO)
583                 obj->data.diskio = prepare_diskio_stat(dev_name(arg));
584         END OBJ(diskio_write, INFO_DISKIO)
585                 obj->data.diskio = prepare_diskio_stat(dev_name(arg));
586 #ifdef X11
587         END OBJ(diskiograph, INFO_DISKIO)
588                 char *buf = 0;
589                 SIZE_DEFAULTS(graph);
590                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
591                                 &obj->e, &obj->char_a, &obj->char_b);
592
593                 obj->data.diskio = prepare_diskio_stat(dev_name(buf));
594                 if (buf) free(buf);
595         END OBJ(diskiograph_read, INFO_DISKIO)
596                 char *buf = 0;
597                 SIZE_DEFAULTS(graph);
598                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
599                                 &obj->e, &obj->char_a, &obj->char_b);
600
601                 obj->data.diskio = prepare_diskio_stat(dev_name(buf));
602                 if (buf) free(buf);
603         END OBJ(diskiograph_write, INFO_DISKIO)
604                 char *buf = 0;
605                 SIZE_DEFAULTS(graph);
606                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
607                                 &obj->e, &obj->char_a, &obj->char_b);
608
609                 obj->data.diskio = prepare_diskio_stat(dev_name(buf));
610                 if (buf) free(buf);
611 #endif /* X11 */
612         END OBJ(color, 0)
613 #ifdef X11
614                 if (output_methods & TO_X) {
615                         obj->data.l = arg ? get_x11_color(arg) : default_fg_color;
616                         set_current_text_color(obj->data.l);
617                 }
618 #endif /* X11 */
619 #ifdef NCURSES
620                 if (output_methods & TO_NCURSES) {
621                         obj->data.l = COLOR_WHITE;
622                         if(arg) {
623                                 if(strcasecmp(arg, "red") == 0) {
624                                         obj->data.l = COLOR_RED;
625                                 }else if(strcasecmp(arg, "green") == 0) {
626                                         obj->data.l = COLOR_GREEN;
627                                 }else if(strcasecmp(arg, "yellow") == 0) {
628                                         obj->data.l = COLOR_YELLOW;
629                                 }else if(strcasecmp(arg, "blue") == 0) {
630                                         obj->data.l = COLOR_BLUE;
631                                 }else if(strcasecmp(arg, "magenta") == 0) {
632                                         obj->data.l = COLOR_MAGENTA;
633                                 }else if(strcasecmp(arg, "cyan") == 0) {
634                                         obj->data.l = COLOR_CYAN;
635                                 }else if(strcasecmp(arg, "black") == 0) {
636                                         obj->data.l = COLOR_BLACK;
637                                 }
638                         }
639                         set_current_text_color(obj->data.l);
640                         init_pair(obj->data.l, obj->data.l, COLOR_BLACK);
641                 }
642 #endif /* NCURSES */
643         END OBJ(color0, 0)
644                 obj->data.l = color0;
645                 set_current_text_color(obj->data.l);
646         END OBJ(color1, 0)
647                 obj->data.l = color1;
648                 set_current_text_color(obj->data.l);
649         END OBJ(color2, 0)
650                 obj->data.l = color2;
651                 set_current_text_color(obj->data.l);
652         END OBJ(color3, 0)
653                 obj->data.l = color3;
654                 set_current_text_color(obj->data.l);
655         END OBJ(color4, 0)
656                 obj->data.l = color4;
657                 set_current_text_color(obj->data.l);
658         END OBJ(color5, 0)
659                 obj->data.l = color5;
660                 set_current_text_color(obj->data.l);
661         END OBJ(color6, 0)
662                 obj->data.l = color6;
663                 set_current_text_color(obj->data.l);
664         END OBJ(color7, 0)
665                 obj->data.l = color7;
666                 set_current_text_color(obj->data.l);
667         END OBJ(color8, 0)
668                 obj->data.l = color8;
669                 set_current_text_color(obj->data.l);
670         END OBJ(color9, 0)
671                 obj->data.l = color9;
672                 set_current_text_color(obj->data.l);
673 #ifdef X11
674         END OBJ(font, 0)
675                 obj->data.s = scan_font(arg);
676 #endif /* X11 */
677         END OBJ(conky_version, 0)
678         END OBJ(conky_build_date, 0)
679         END OBJ(conky_build_arch, 0)
680         END OBJ(downspeed, INFO_NET)
681                 if (arg) {
682                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
683                 } else {
684                         // default to DEFAULTNETDEV
685                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
686                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
687                         free(buf);
688                 }
689         END OBJ(downspeedf, INFO_NET)
690                 if (arg) {
691                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
692                 } else {
693                         // default to DEFAULTNETDEV
694                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
695                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
696                         free(buf);
697                 }
698 #ifdef X11
699         END OBJ(downspeedgraph, INFO_NET)
700                 char *buf = 0;
701                 SIZE_DEFAULTS(graph);
702                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
703                                 &obj->e, &obj->char_a, &obj->char_b);
704
705                 // default to DEFAULTNETDEV
706                 buf = strndup(buf ? buf : DEFAULTNETDEV, text_buffer_size);
707                 obj->data.net = get_net_stat(buf, obj, free_at_crash);
708                 free(buf);
709 #endif /* X11 */
710         END OBJ(else, 0)
711                 obj_be_ifblock_else(ifblock_opaque, obj);
712         END OBJ(endif, 0)
713                 obj_be_ifblock_endif(ifblock_opaque, obj);
714         END OBJ(eval, 0)
715                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
716         END OBJ(image, 0)
717                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
718         END OBJ(exec, 0)
719                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
720         END OBJ(execp, 0)
721                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
722         END OBJ(execbar, 0)
723                 SIZE_DEFAULTS(bar);
724                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
725 #ifdef X11
726         END OBJ(execgauge, 0)
727                 SIZE_DEFAULTS(gauge);
728                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
729         END OBJ(execgraph, 0)
730                 SIZE_DEFAULTS(graph);
731                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
732 #endif /* X11 */
733         END OBJ(execibar, 0)
734                 int n;
735                 SIZE_DEFAULTS(bar);
736
737                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
738                         char buf[256];
739
740                         NORM_ERR("${execibar <interval> command}");
741                         obj->type = OBJ_text;
742                         snprintf(buf, 256, "${%s}", s);
743                         obj->data.s = strndup(buf, text_buffer_size);
744                 } else {
745                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
746                 }
747 #ifdef X11
748         END OBJ(execigraph, 0)
749                 int n;
750                 SIZE_DEFAULTS(graph);
751
752                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
753                         char buf[256];
754
755                         NORM_ERR("${execigraph <interval> command}");
756                         obj->type = OBJ_text;
757                         snprintf(buf, 256, "${%s}", s);
758                         obj->data.s = strndup(buf, text_buffer_size);
759                 } else {
760                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
761                 }
762         END OBJ(execigauge, 0)
763                 int n;
764                 SIZE_DEFAULTS(gauge);
765
766                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
767                         char buf[256];
768
769                         NORM_ERR("${execigauge <interval> command}");
770                         obj->type = OBJ_text;
771                         snprintf(buf, 256, "${%s}", s);
772                         obj->data.s = strndup(buf, text_buffer_size);
773                 } else {
774                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
775                 }
776 #endif /* X11 */
777         END OBJ(execi, 0)
778                 int n;
779
780                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
781                         char buf[256];
782
783                         NORM_ERR("${execi <interval> command}");
784                         obj->type = OBJ_text;
785                         snprintf(buf, 256, "${%s}", s);
786                         obj->data.s = strndup(buf, text_buffer_size);
787                 } else {
788                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
789                         obj->data.execi.buffer = malloc(text_buffer_size);
790                 }
791         END OBJ(execpi, 0)
792                 int n;
793
794                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
795                         char buf[256];
796
797                         NORM_ERR("${execi <interval> command}");
798                         obj->type = OBJ_text;
799                         snprintf(buf, 256, "${%s}", s);
800                         obj->data.s = strndup(buf, text_buffer_size);
801                 } else {
802                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
803                         obj->data.execi.buffer = malloc(text_buffer_size);
804                 }
805         END OBJ(texeci, 0)
806                         int n;
807
808                         if (!arg || sscanf(arg, "%f %n", &obj->data.texeci.interval, &n) <= 0) {
809                                 char buf[256];
810
811                                 NORM_ERR("${texeci <interval> command}");
812                                 obj->type = OBJ_text;
813                                 snprintf(buf, 256, "${%s}", s);
814                                 obj->data.s = strndup(buf, text_buffer_size);
815                         } else {
816                                 obj->data.texeci.cmd = strndup(arg + n, text_buffer_size);
817                                 obj->data.texeci.buffer = malloc(text_buffer_size);
818                         }
819                         obj->data.texeci.p_timed_thread = NULL;
820         END OBJ(pre_exec, 0)
821                 obj->type = OBJ_text;
822                 if (arg) {
823                         char buf[2048];
824
825                         do_read_exec(arg, buf, sizeof(buf));
826                         obj->data.s = strndup(buf, text_buffer_size);
827                 } else {
828                         obj->data.s = strndup("", text_buffer_size);
829                 }
830         END OBJ(fs_bar, INFO_FS)
831                 SIZE_DEFAULTS(bar);
832                 arg = scan_bar(arg, &obj->data.fsbar.w, &obj->data.fsbar.h);
833                 if (arg) {
834                         while (isspace(*arg)) {
835                                 arg++;
836                         }
837                         if (*arg == '\0') {
838                                 arg = "/";
839                         }
840                 } else {
841                         arg = "/";
842                 }
843                 obj->data.fsbar.fs = prepare_fs_stat(arg);
844         END OBJ(fs_bar_free, INFO_FS)
845                 SIZE_DEFAULTS(bar);
846                 arg = scan_bar(arg, &obj->data.fsbar.w, &obj->data.fsbar.h);
847                 if (arg) {
848                         while (isspace(*arg)) {
849                                 arg++;
850                         }
851                         if (*arg == '\0') {
852                                 arg = "/";
853                         }
854                 } else {
855                         arg = "/";
856                 }
857
858                 obj->data.fsbar.fs = prepare_fs_stat(arg);
859         END OBJ(fs_free, INFO_FS)
860                 if (!arg) {
861                         arg = "/";
862                 }
863                 obj->data.fs = prepare_fs_stat(arg);
864         END OBJ(fs_used_perc, INFO_FS)
865                 if (!arg) {
866                         arg = "/";
867                 }
868                 obj->data.fs = prepare_fs_stat(arg);
869         END OBJ(fs_free_perc, INFO_FS)
870                 if (!arg) {
871                         arg = "/";
872                 }
873                 obj->data.fs = prepare_fs_stat(arg);
874         END OBJ(fs_size, INFO_FS)
875                 if (!arg) {
876                         arg = "/";
877                 }
878                 obj->data.fs = prepare_fs_stat(arg);
879         END OBJ(fs_type, INFO_FS)
880                 if (!arg) {
881                         arg = "/";
882                 }
883                 obj->data.fs = prepare_fs_stat(arg);
884         END OBJ(fs_used, INFO_FS)
885                 if (!arg) {
886                         arg = "/";
887                 }
888                 obj->data.fs = prepare_fs_stat(arg);
889         END OBJ(hr, 0)
890                 obj->data.i = arg ? atoi(arg) : 1;
891         END OBJ(nameserver, INFO_DNS)
892                 obj->data.i = arg ? atoi(arg) : 0;
893         END OBJ(offset, 0)
894                 obj->data.i = arg ? atoi(arg) : 1;
895         END OBJ(voffset, 0)
896                 obj->data.i = arg ? atoi(arg) : 1;
897         END OBJ(goto, 0)
898
899                 if (!arg) {
900                         NORM_ERR("goto needs arguments");
901                         obj->type = OBJ_text;
902                         obj->data.s = strndup("${goto}", text_buffer_size);
903                         return NULL;
904                 }
905
906                 obj->data.i = atoi(arg);
907
908         END OBJ(tab, 0)
909                 int a = 10, b = 0;
910
911                 if (arg) {
912                         if (sscanf(arg, "%d %d", &a, &b) != 2) {
913                                 sscanf(arg, "%d", &b);
914                         }
915                 }
916                 if (a <= 0) {
917                         a = 1;
918                 }
919                 obj->data.pair.a = a;
920                 obj->data.pair.b = b;
921
922 #ifdef __linux__
923         END OBJ(i2c, INFO_SYSFS)
924                 char buf1[64], buf2[64];
925                 float factor, offset;
926                 int n, found = 0;
927
928                 if (!arg) {
929                         NORM_ERR("i2c needs arguments");
930                         obj->type = OBJ_text;
931                         // obj->data.s = strndup("${i2c}", text_buffer_size);
932                         return NULL;
933                 }
934
935 #define HWMON_RESET() {\
936                 buf1[0] = 0; \
937                 factor = 1.0; \
938                 offset = 0.0; }
939
940                 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
941                 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
942                 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
943                 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
944
945                 if (!found) {
946                         NORM_ERR("i2c failed to parse arguments");
947                         obj->type = OBJ_text;
948                         return NULL;
949                 }
950                 DBGP("parsed i2c args: '%s' '%s' %d %f %f\n", buf1, buf2, n, factor, offset);
951                 obj->data.sysfs.fd = open_i2c_sensor((*buf1) ? buf1 : 0, buf2, n,
952                                 &obj->data.sysfs.arg, obj->data.sysfs.devtype);
953                 strncpy(obj->data.sysfs.type, buf2, 63);
954                 obj->data.sysfs.factor = factor;
955                 obj->data.sysfs.offset = offset;
956
957         END OBJ(platform, INFO_SYSFS)
958                 char buf1[64], buf2[64];
959                 float factor, offset;
960                 int n, found = 0;
961
962                 if (!arg) {
963                         NORM_ERR("platform needs arguments");
964                         obj->type = OBJ_text;
965                         return NULL;
966                 }
967
968                 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
969                 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
970                 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
971                 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
972
973                 if (!found) {
974                         NORM_ERR("platform failed to parse arguments");
975                         obj->type = OBJ_text;
976                         return NULL;
977                 }
978                 DBGP("parsed platform args: '%s' '%s' %d %f %f", buf1, buf2, n, factor, offset);
979                 obj->data.sysfs.fd = open_platform_sensor((*buf1) ? buf1 : 0, buf2, n,
980                                 &obj->data.sysfs.arg, obj->data.sysfs.devtype);
981                 strncpy(obj->data.sysfs.type, buf2, 63);
982                 obj->data.sysfs.factor = factor;
983                 obj->data.sysfs.offset = offset;
984
985         END OBJ(hwmon, INFO_SYSFS)
986                 char buf1[64], buf2[64];
987                 float factor, offset;
988                 int n, found = 0;
989
990                 if (!arg) {
991                         NORM_ERR("hwmon needs argumanets");
992                         obj->type = OBJ_text;
993                         return NULL;
994                 }
995
996                 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
997                 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
998                 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
999                 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1000
1001 #undef HWMON_RESET
1002
1003                 if (!found) {
1004                         NORM_ERR("hwmon failed to parse arguments");
1005                         obj->type = OBJ_text;
1006                         return NULL;
1007                 }
1008                 DBGP("parsed hwmon args: '%s' '%s' %d %f %f\n", buf1, buf2, n, factor, offset);
1009                 obj->data.sysfs.fd = open_hwmon_sensor((*buf1) ? buf1 : 0, buf2, n,
1010                                 &obj->data.sysfs.arg, obj->data.sysfs.devtype);
1011                 strncpy(obj->data.sysfs.type, buf2, 63);
1012                 obj->data.sysfs.factor = factor;
1013                 obj->data.sysfs.offset = offset;
1014
1015 #endif /* !__OpenBSD__ */
1016
1017         END
1018         /* we have four different types of top (top, top_mem, top_time and top_io). To
1019          * avoid having almost-same code four times, we have this special
1020          * handler. */
1021         if (strncmp(s, "top", 3) == EQUAL) {
1022                 if (!parse_top_args(s, arg, obj)) {
1023                         return NULL;
1024                 }
1025         } else OBJ(addr, INFO_NET)
1026                 if (arg) {
1027                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
1028                 } else {
1029                         // default to DEFAULTNETDEV
1030                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
1031                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
1032                         free(buf);
1033                 }
1034 #if defined(__linux__)
1035         END OBJ(addrs, INFO_NET)
1036                 if (arg) {
1037                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
1038                 } else {
1039                         // default to DEFAULTNETDEV
1040                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
1041                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
1042                         free(buf);
1043                 }
1044 #endif /* __linux__ */
1045         END OBJ(tail, 0)
1046                 init_tailhead("tail", arg, obj, free_at_crash);
1047         END OBJ(head, 0)
1048                 init_tailhead("head", arg, obj, free_at_crash);
1049         END OBJ(lines, 0)
1050                 if (arg) {
1051                         obj->data.s = strndup(arg, text_buffer_size);
1052                 }else{
1053                         CRIT_ERR(obj, free_at_crash, "lines needs a argument");
1054                 }
1055         END OBJ(words, 0)
1056                 if (arg) {
1057                         obj->data.s = strndup(arg, text_buffer_size);
1058                 }else{
1059                         CRIT_ERR(obj, free_at_crash, "words needs a argument");
1060                 }
1061         END OBJ(loadavg, INFO_LOADAVG)
1062                 int a = 1, b = 2, c = 3, r = 3;
1063
1064                 if (arg) {
1065                         r = sscanf(arg, "%d %d %d", &a, &b, &c);
1066                         if (r >= 3 && (c < 1 || c > 3)) {
1067                                 r--;
1068                         }
1069                         if (r >= 2 && (b < 1 || b > 3)) {
1070                                 r--, b = c;
1071                         }
1072                         if (r >= 1 && (a < 1 || a > 3)) {
1073                                 r--, a = b, b = c;
1074                         }
1075                 }
1076                 obj->data.loadavg[0] = (r >= 1) ? (unsigned char) a : 0;
1077                 obj->data.loadavg[1] = (r >= 2) ? (unsigned char) b : 0;
1078                 obj->data.loadavg[2] = (r >= 3) ? (unsigned char) c : 0;
1079         END OBJ_IF(if_empty, 0)
1080                 if (!arg) {
1081                         NORM_ERR("if_empty needs an argument");
1082                         obj->data.ifblock.s = 0;
1083                 } else {
1084                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
1085                         obj->sub = malloc(sizeof(struct text_object));
1086                         extract_variable_text_internal(obj->sub,
1087                                                        obj->data.ifblock.s);
1088                 }
1089         END OBJ_IF(if_match, 0)
1090                 if (!arg) {
1091                         NORM_ERR("if_match needs arguments");
1092                         obj->data.ifblock.s = 0;
1093                 } else {
1094                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
1095                         obj->sub = malloc(sizeof(struct text_object));
1096                         extract_variable_text_internal(obj->sub,
1097                                                        obj->data.ifblock.s);
1098                 }
1099         END OBJ_IF(if_existing, 0)
1100                 if (!arg) {
1101                         NORM_ERR("if_existing needs an argument or two");
1102                         obj->data.ifblock.s = NULL;
1103                         obj->data.ifblock.str = NULL;
1104                 } else {
1105                         char buf1[256], buf2[256];
1106                         int r = sscanf(arg, "%255s %255[^\n]", buf1, buf2);
1107
1108                         if (r == 1) {
1109                                 obj->data.ifblock.s = strndup(buf1, text_buffer_size);
1110                                 obj->data.ifblock.str = NULL;
1111                         } else {
1112                                 obj->data.ifblock.s = strndup(buf1, text_buffer_size);
1113                                 obj->data.ifblock.str = strndup(buf2, text_buffer_size);
1114                         }
1115                 }
1116                 DBGP("if_existing: '%s' '%s'", obj->data.ifblock.s, obj->data.ifblock.str);
1117         END OBJ_IF(if_mounted, 0)
1118                 if (!arg) {
1119                         NORM_ERR("if_mounted needs an argument");
1120                         obj->data.ifblock.s = 0;
1121                 } else {
1122                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
1123                 }
1124 #ifdef __linux__
1125         END OBJ_IF(if_running, INFO_TOP)
1126                 if (arg) {
1127                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
1128 #else
1129         END OBJ_IF(if_running, 0)
1130                 if (arg) {
1131                         char buf[256];
1132
1133                         snprintf(buf, 256, "pidof %s >/dev/null", arg);
1134                         obj->data.ifblock.s = strndup(buf, text_buffer_size);
1135 #endif
1136                 } else {
1137                         NORM_ERR("if_running needs an argument");
1138                         obj->data.ifblock.s = 0;
1139                 }
1140         END OBJ(kernel, 0)
1141         END OBJ(machine, 0)
1142         END OBJ(mails, 0)
1143                 float n1;
1144                 char mbox[256], dst[256];
1145
1146                 if (!arg) {
1147                         n1 = 9.5;
1148                         /* Kapil: Changed from MAIL_FILE to
1149                            current_mail_spool since the latter
1150                            is a copy of the former if undefined
1151                            but the latter should take precedence
1152                            if defined */
1153                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1154                 } else {
1155                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1156                                 n1 = 9.5;
1157                                 strncpy(mbox, arg, sizeof(mbox));
1158                         }
1159                 }
1160
1161                 variable_substitute(mbox, dst, sizeof(dst));
1162                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1163                 obj->data.local_mail.interval = n1;
1164         END OBJ(new_mails, 0)
1165                 float n1;
1166                 char mbox[256], dst[256];
1167
1168                 if (!arg) {
1169                         n1 = 9.5;
1170                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1171                 } else {
1172                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1173                                 n1 = 9.5;
1174                                 strncpy(mbox, arg, sizeof(mbox));
1175                         }
1176                 }
1177
1178                 variable_substitute(mbox, dst, sizeof(dst));
1179                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1180                 obj->data.local_mail.interval = n1;
1181         END OBJ(seen_mails, 0)
1182                 float n1;
1183                 char mbox[256], dst[256];
1184
1185                 if (!arg) {
1186                         n1 = 9.5;
1187                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1188                 } else {
1189                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1190                                 n1 = 9.5;
1191                                 strncpy(mbox, arg, sizeof(mbox));
1192                         }
1193                 }
1194
1195                 variable_substitute(mbox, dst, sizeof(dst));
1196                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1197                 obj->data.local_mail.interval = n1;
1198         END OBJ(unseen_mails, 0)
1199                 float n1;
1200                 char mbox[256], dst[256];
1201
1202                 if (!arg) {
1203                         n1 = 9.5;
1204                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1205                 } else {
1206                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1207                                 n1 = 9.5;
1208                                 strncpy(mbox, arg, sizeof(mbox));
1209                         }
1210                 }
1211
1212                 variable_substitute(mbox, dst, sizeof(dst));
1213                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1214                 obj->data.local_mail.interval = n1;
1215         END OBJ(flagged_mails, 0)
1216                 float n1;
1217                 char mbox[256], dst[256];
1218
1219                 if (!arg) {
1220                         n1 = 9.5;
1221                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1222                 } else {
1223                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1224                                 n1 = 9.5;
1225                                 strncpy(mbox, arg, sizeof(mbox));
1226                         }
1227                 }
1228
1229                 variable_substitute(mbox, dst, sizeof(dst));
1230                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1231                 obj->data.local_mail.interval = n1;
1232         END OBJ(unflagged_mails, 0)
1233                 float n1;
1234                 char mbox[256], dst[256];
1235
1236                 if (!arg) {
1237                         n1 = 9.5;
1238                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1239                 } else {
1240                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1241                                 n1 = 9.5;
1242                                 strncpy(mbox, arg, sizeof(mbox));
1243                         }
1244                 }
1245
1246                 variable_substitute(mbox, dst, sizeof(dst));
1247                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1248                 obj->data.local_mail.interval = n1;
1249         END OBJ(forwarded_mails, 0)
1250                 float n1;
1251                 char mbox[256], dst[256];
1252
1253                 if (!arg) {
1254                         n1 = 9.5;
1255                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1256                 } else {
1257                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1258                                 n1 = 9.5;
1259                                 strncpy(mbox, arg, sizeof(mbox));
1260                         }
1261                 }
1262
1263                 variable_substitute(mbox, dst, sizeof(dst));
1264                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1265                 obj->data.local_mail.interval = n1;
1266         END OBJ(unforwarded_mails, 0)
1267                 float n1;
1268                 char mbox[256], dst[256];
1269
1270                 if (!arg) {
1271                         n1 = 9.5;
1272                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1273                 } else {
1274                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1275                                 n1 = 9.5;
1276                                 strncpy(mbox, arg, sizeof(mbox));
1277                         }
1278                 }
1279
1280                 variable_substitute(mbox, dst, sizeof(dst));
1281                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1282                 obj->data.local_mail.interval = n1;
1283         END OBJ(replied_mails, 0)
1284                 float n1;
1285                 char mbox[256], dst[256];
1286
1287                 if (!arg) {
1288                         n1 = 9.5;
1289                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1290                 } else {
1291                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1292                                 n1 = 9.5;
1293                                 strncpy(mbox, arg, sizeof(mbox));
1294                         }
1295                 }
1296
1297                 variable_substitute(mbox, dst, sizeof(dst));
1298                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1299                 obj->data.local_mail.interval = n1;
1300         END OBJ(unreplied_mails, 0)
1301                 float n1;
1302                 char mbox[256], dst[256];
1303
1304                 if (!arg) {
1305                         n1 = 9.5;
1306                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1307                 } else {
1308                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1309                                 n1 = 9.5;
1310                                 strncpy(mbox, arg, sizeof(mbox));
1311                         }
1312                 }
1313
1314                 variable_substitute(mbox, dst, sizeof(dst));
1315                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1316                 obj->data.local_mail.interval = n1;
1317         END OBJ(draft_mails, 0)
1318                 float n1;
1319                 char mbox[256], dst[256];
1320
1321                 if (!arg) {
1322                         n1 = 9.5;
1323                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1324                 } else {
1325                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1326                                 n1 = 9.5;
1327                                 strncpy(mbox, arg, sizeof(mbox));
1328                         }
1329                 }
1330
1331                 variable_substitute(mbox, dst, sizeof(dst));
1332                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1333                 obj->data.local_mail.interval = n1;
1334         END OBJ(trashed_mails, 0)
1335                 float n1;
1336                 char mbox[256], dst[256];
1337
1338                 if (!arg) {
1339                         n1 = 9.5;
1340                         strncpy(mbox, current_mail_spool, sizeof(mbox));
1341                 } else {
1342                         if (sscanf(arg, "%s %f", mbox, &n1) != 2) {
1343                                 n1 = 9.5;
1344                                 strncpy(mbox, arg, sizeof(mbox));
1345                         }
1346                 }
1347
1348                 variable_substitute(mbox, dst, sizeof(dst));
1349                 obj->data.local_mail.mbox = strndup(dst, text_buffer_size);
1350                 obj->data.local_mail.interval = n1;
1351         END OBJ(mboxscan, 0)
1352                 obj->data.mboxscan.args = (char *) malloc(text_buffer_size);
1353                 obj->data.mboxscan.output = (char *) malloc(text_buffer_size);
1354                 /* if '1' (in mboxscan.c) then there was SIGUSR1, hmm */
1355                 obj->data.mboxscan.output[0] = 1;
1356                 strncpy(obj->data.mboxscan.args, arg, text_buffer_size);
1357         END OBJ(mem, INFO_MEM)
1358         END OBJ(memeasyfree, INFO_MEM)
1359         END OBJ(memfree, INFO_MEM)
1360         END OBJ(memmax, INFO_MEM)
1361         END OBJ(memperc, INFO_MEM)
1362 #ifdef X11
1363         END OBJ(memgauge, INFO_MEM)
1364                 SIZE_DEFAULTS(gauge);
1365                 scan_gauge(arg, &obj->data.pair.a, &obj->data.pair.b);
1366 #endif /* X11*/
1367         END OBJ(membar, INFO_MEM)
1368                 SIZE_DEFAULTS(bar);
1369                 scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
1370 #ifdef X11
1371         END OBJ(memgraph, INFO_MEM)
1372                 char *buf = 0;
1373                 SIZE_DEFAULTS(graph);
1374                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
1375                                 &obj->e, &obj->char_a, &obj->char_b);
1376
1377                 if (buf) free(buf);
1378 #endif /* X11*/
1379         END OBJ(mixer, INFO_MIXER)
1380                 obj->data.l = mixer_init(arg);
1381         END OBJ(mixerl, INFO_MIXER)
1382                 obj->data.l = mixer_init(arg);
1383         END OBJ(mixerr, INFO_MIXER)
1384                 obj->data.l = mixer_init(arg);
1385 #ifdef X11
1386         END OBJ(mixerbar, INFO_MIXER)
1387                 SIZE_DEFAULTS(bar);
1388                 scan_mixer_bar(arg, &obj->data.mixerbar.l, &obj->data.mixerbar.w,
1389                         &obj->data.mixerbar.h);
1390         END OBJ(mixerlbar, INFO_MIXER)
1391                 SIZE_DEFAULTS(bar);
1392                 scan_mixer_bar(arg, &obj->data.mixerbar.l, &obj->data.mixerbar.w,
1393                         &obj->data.mixerbar.h);
1394         END OBJ(mixerrbar, INFO_MIXER)
1395                 SIZE_DEFAULTS(bar);
1396                 scan_mixer_bar(arg, &obj->data.mixerbar.l, &obj->data.mixerbar.w,
1397                         &obj->data.mixerbar.h);
1398 #endif
1399         END OBJ_IF(if_mixer_mute, INFO_MIXER)
1400                 obj->data.ifblock.i = mixer_init(arg);
1401 #ifdef X11
1402         END OBJ(monitor, INFO_X11)
1403         END OBJ(monitor_number, INFO_X11)
1404         END OBJ(desktop, INFO_X11)
1405         END OBJ(desktop_number, INFO_X11)
1406         END OBJ(desktop_name, INFO_X11)
1407 #endif
1408         END OBJ(nodename, 0)
1409         END OBJ(processes, INFO_PROCS)
1410         END OBJ(running_processes, INFO_RUN_PROCS)
1411         END OBJ(shadecolor, 0)
1412 #ifdef X11
1413                 obj->data.l = arg ? get_x11_color(arg) : default_bg_color;
1414 #endif /* X11 */
1415         END OBJ(outlinecolor, 0)
1416 #ifdef X11
1417                 obj->data.l = arg ? get_x11_color(arg) : default_out_color;
1418 #endif /* X11 */
1419         END OBJ(stippled_hr, 0)
1420 #ifdef X11
1421                 int a = get_stippled_borders(), b = 1;
1422
1423                 if (arg) {
1424                         if (sscanf(arg, "%d %d", &a, &b) != 2) {
1425                                 sscanf(arg, "%d", &b);
1426                         }
1427                 }
1428                 if (a <= 0) {
1429                         a = 1;
1430                 }
1431                 obj->data.pair.a = a;
1432                 obj->data.pair.b = b;
1433 #endif /* X11 */
1434         END OBJ(swap, INFO_MEM)
1435         END OBJ(swapfree, INFO_MEM)
1436         END OBJ(swapmax, INFO_MEM)
1437         END OBJ(swapperc, INFO_MEM)
1438         END OBJ(swapbar, INFO_MEM)
1439                 SIZE_DEFAULTS(bar);
1440                 scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
1441         END OBJ(sysname, 0)
1442         END OBJ(time, 0)
1443                 obj->data.s = strndup(arg ? arg : "%F %T", text_buffer_size);
1444         END OBJ(utime, 0)
1445                 obj->data.s = strndup(arg ? arg : "%F %T", text_buffer_size);
1446         END OBJ(tztime, 0)
1447                 char buf1[256], buf2[256], *fmt, *tz;
1448
1449                 fmt = tz = NULL;
1450                 if (arg) {
1451                         int nArgs = sscanf(arg, "%255s %255[^\n]", buf1, buf2);
1452
1453                         switch (nArgs) {
1454                                 case 2:
1455                                         tz = buf1;
1456                                 case 1:
1457                                         fmt = buf2;
1458                         }
1459                 }
1460
1461                 obj->data.tztime.fmt = strndup(fmt ? fmt : "%F %T", text_buffer_size);
1462                 obj->data.tztime.tz = tz ? strndup(tz, text_buffer_size) : NULL;
1463 #ifdef HAVE_ICONV
1464         END OBJ(iconv_start, 0)
1465                 if (is_iconv_converting()) {
1466                         CRIT_ERR(obj, free_at_crash, "You must stop your last iconv conversion before "
1467                                 "starting another");
1468                 }
1469                 if (arg) {
1470                         char iconv_from[ICONV_CODEPAGE_LENGTH];
1471                         char iconv_to[ICONV_CODEPAGE_LENGTH];
1472
1473                         if (sscanf(arg, "%s %s", iconv_from, iconv_to) != 2) {
1474                                 CRIT_ERR(obj, free_at_crash, "Invalid arguments for iconv_start");
1475                         } else {
1476                                 iconv_t new_iconv;
1477
1478                                 new_iconv = iconv_open(iconv_to, iconv_from);
1479                                 if (new_iconv == (iconv_t) (-1)) {
1480                                         NORM_ERR("Can't convert from %s to %s.", iconv_from, iconv_to);
1481                                 } else {
1482                                         obj->a = register_iconv(&new_iconv);
1483                                         set_iconv_converting(1);
1484                                 }
1485                         }
1486                 } else {
1487                         CRIT_ERR(obj, free_at_crash, "Iconv requires arguments");
1488                 }
1489         END OBJ(iconv_stop, 0)
1490                 set_iconv_converting(0);
1491
1492 #endif
1493         END OBJ(totaldown, INFO_NET)
1494                 if (arg) {
1495                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
1496                 } else {
1497                         // default to DEFAULTNETDEV
1498                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
1499                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
1500                         free(buf);
1501                 }
1502         END OBJ(totalup, INFO_NET)
1503                 obj->data.net = get_net_stat(arg, obj, free_at_crash);
1504                 if (arg) {
1505                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
1506                 } else {
1507                         // default to DEFAULTNETDEV
1508                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
1509                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
1510                         free(buf);
1511                 }
1512         END OBJ(updates, 0)
1513         END OBJ_IF(if_updatenr, 0)
1514                 obj->data.ifblock.i = arg ? atoi(arg) : 0;
1515                 if(obj->data.ifblock.i == 0) CRIT_ERR(obj, free_at_crash, "if_updatenr needs a number above 0 as argument");
1516                 set_updatereset(obj->data.ifblock.i > get_updatereset() ? obj->data.ifblock.i : get_updatereset());
1517         END OBJ(alignr, 0)
1518                 obj->data.i = arg ? atoi(arg) : 0;
1519         END OBJ(alignc, 0)
1520                 obj->data.i = arg ? atoi(arg) : 0;
1521         END OBJ(upspeed, INFO_NET)
1522                 if (arg) {
1523                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
1524                 } else {
1525                         // default to DEFAULTNETDEV
1526                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
1527                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
1528                         free(buf);
1529                 }
1530         END OBJ(upspeedf, INFO_NET)
1531                 if (arg) {
1532                         obj->data.net = get_net_stat(arg, obj, free_at_crash);
1533                 } else {
1534                         // default to DEFAULTNETDEV
1535                         char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
1536                         obj->data.net = get_net_stat(buf, obj, free_at_crash);
1537                         free(buf);
1538                 }
1539
1540 #ifdef X11
1541         END OBJ(upspeedgraph, INFO_NET)
1542                 char *buf = 0;
1543                 SIZE_DEFAULTS(graph);
1544                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
1545                                 &obj->e, &obj->char_a, &obj->char_b);
1546
1547                 // default to DEFAULTNETDEV
1548                 buf = strndup(buf ? buf : DEFAULTNETDEV, text_buffer_size);
1549                 obj->data.net = get_net_stat(buf, obj, free_at_crash);
1550                 free(buf);
1551 #endif
1552         END OBJ(uptime_short, INFO_UPTIME)
1553         END OBJ(uptime, INFO_UPTIME)
1554         END OBJ(user_names, INFO_USERS)
1555         END OBJ(user_times, INFO_USERS)
1556         END OBJ(user_terms, INFO_USERS)
1557         END OBJ(user_number, INFO_USERS)
1558 #if defined(__linux__)
1559         END OBJ(gw_iface, INFO_GW)
1560         END OBJ(gw_ip, INFO_GW)
1561 #endif /* !__linux__ */
1562 #ifndef __OpenBSD__
1563         END OBJ(adt746xcpu, 0)
1564         END OBJ(adt746xfan, 0)
1565 #endif /* !__OpenBSD__ */
1566 #if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \
1567                 || defined(__OpenBSD__)) && (defined(i386) || defined(__i386__))
1568         END OBJ(apm_adapter, 0)
1569         END OBJ(apm_battery_life, 0)
1570         END OBJ(apm_battery_time, 0)
1571 #endif /* __FreeBSD__ */
1572         END OBJ(imap_unseen, 0)
1573                 if (arg) {
1574                         // proccss
1575                         obj->data.mail = parse_mail_args(IMAP_TYPE, arg);
1576                         obj->char_b = 0;
1577                 } else {
1578                         obj->char_b = 1;
1579                 }
1580         END OBJ(imap_messages, 0)
1581                 if (arg) {
1582                         // proccss
1583                         obj->data.mail = parse_mail_args(IMAP_TYPE, arg);
1584                         obj->char_b = 0;
1585                 } else {
1586                         obj->char_b = 1;
1587                 }
1588         END OBJ(pop3_unseen, 0)
1589                 if (arg) {
1590                         // proccss
1591                         obj->data.mail = parse_mail_args(POP3_TYPE, arg);
1592                         obj->char_b = 0;
1593                 } else {
1594                         obj->char_b = 1;
1595                 }
1596         END OBJ(pop3_used, 0)
1597                 if (arg) {
1598                         // proccss
1599                         obj->data.mail = parse_mail_args(POP3_TYPE, arg);
1600                         obj->char_b = 0;
1601                 } else {
1602                         obj->char_b = 1;
1603                 }
1604 #ifdef IBM
1605         END OBJ(smapi, 0)
1606                 if (arg)
1607                         obj->data.s = strndup(arg, text_buffer_size);
1608                 else
1609                         NORM_ERR("smapi needs an argument");
1610         END OBJ_IF(if_smapi_bat_installed, 0)
1611                 if (!arg) {
1612                         NORM_ERR("if_smapi_bat_installed needs an argument");
1613                         obj->data.ifblock.s = 0;
1614                 } else
1615                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
1616         END OBJ(smapi_bat_perc, 0)
1617                 if (arg)
1618                         obj->data.s = strndup(arg, text_buffer_size);
1619                 else
1620                         NORM_ERR("smapi_bat_perc needs an argument");
1621         END OBJ(smapi_bat_temp, 0)
1622                 if (arg)
1623                         obj->data.s = strndup(arg, text_buffer_size);
1624                 else
1625                         NORM_ERR("smapi_bat_temp needs an argument");
1626         END OBJ(smapi_bat_power, 0)
1627                 if (arg)
1628                         obj->data.s = strndup(arg, text_buffer_size);
1629                 else
1630                         NORM_ERR("smapi_bat_power needs an argument");
1631 #ifdef X11
1632         END OBJ(smapi_bat_bar, 0)
1633                 SIZE_DEFAULTS(bar);
1634                 if(arg) {
1635                         int cnt;
1636                         if(sscanf(arg, "%i %n", &obj->data.i, &cnt) <= 0) {
1637                                 NORM_ERR("first argument to smapi_bat_bar must be an integer value");
1638                                 obj->data.i = -1;
1639                         } else {
1640                                 obj->b = 4;
1641                                 arg = scan_bar(arg + cnt, &obj->a, &obj->b);
1642                         }
1643                 } else
1644                         NORM_ERR("smapi_bat_bar needs an argument");
1645 #endif /* X11 */
1646 #endif /* IBM */
1647 #ifdef MPD
1648 #define mpd_set_maxlen(name) \
1649                 if (arg) { \
1650                         int i; \
1651                         sscanf(arg, "%d", &i); \
1652                         if (i > 0) \
1653                                 obj->data.i = i + 1; \
1654                         else \
1655                                 NORM_ERR(#name ": invalid length argument"); \
1656                 }
1657         END OBJ(mpd_artist, INFO_MPD)
1658                 mpd_set_maxlen(mpd_artist);
1659                 init_mpd();
1660         END OBJ(mpd_title, INFO_MPD)
1661                 mpd_set_maxlen(mpd_title);
1662                 init_mpd();
1663         END OBJ(mpd_random, INFO_MPD) init_mpd();
1664         END OBJ(mpd_repeat, INFO_MPD) init_mpd();
1665         END OBJ(mpd_elapsed, INFO_MPD) init_mpd();
1666         END OBJ(mpd_length, INFO_MPD) init_mpd();
1667         END OBJ(mpd_track, INFO_MPD)
1668                 mpd_set_maxlen(mpd_track);
1669                 init_mpd();
1670         END OBJ(mpd_name, INFO_MPD)
1671                 mpd_set_maxlen(mpd_name);
1672                 init_mpd();
1673         END OBJ(mpd_file, INFO_MPD)
1674                 mpd_set_maxlen(mpd_file);
1675                 init_mpd();
1676         END OBJ(mpd_percent, INFO_MPD) init_mpd();
1677         END OBJ(mpd_album, INFO_MPD)
1678                 mpd_set_maxlen(mpd_album);
1679                 init_mpd();
1680         END OBJ(mpd_vol, INFO_MPD) init_mpd();
1681         END OBJ(mpd_bitrate, INFO_MPD) init_mpd();
1682         END OBJ(mpd_status, INFO_MPD) init_mpd();
1683         END OBJ(mpd_bar, INFO_MPD)
1684                 SIZE_DEFAULTS(bar);
1685                 scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
1686                 init_mpd();
1687         END OBJ(mpd_smart, INFO_MPD)
1688                 mpd_set_maxlen(mpd_smart);
1689                 init_mpd();
1690         END OBJ_IF(if_mpd_playing, INFO_MPD)
1691                 init_mpd();
1692 #undef mpd_set_maxlen
1693 #endif /* MPD */
1694 #ifdef MOC
1695         END OBJ(moc_state, INFO_MOC)
1696         END OBJ(moc_file, INFO_MOC)
1697         END OBJ(moc_title, INFO_MOC)
1698         END OBJ(moc_artist, INFO_MOC)
1699         END OBJ(moc_song, INFO_MOC)
1700         END OBJ(moc_album, INFO_MOC)
1701         END OBJ(moc_totaltime, INFO_MOC)
1702         END OBJ(moc_timeleft, INFO_MOC)
1703         END OBJ(moc_curtime, INFO_MOC)
1704         END OBJ(moc_bitrate, INFO_MOC)
1705         END OBJ(moc_rate, INFO_MOC)
1706 #endif /* MOC */
1707 #ifdef XMMS2
1708         END OBJ(xmms2_artist, INFO_XMMS2)
1709         END OBJ(xmms2_album, INFO_XMMS2)
1710         END OBJ(xmms2_title, INFO_XMMS2)
1711         END OBJ(xmms2_genre, INFO_XMMS2)
1712         END OBJ(xmms2_comment, INFO_XMMS2)
1713         END OBJ(xmms2_url, INFO_XMMS2)
1714         END OBJ(xmms2_tracknr, INFO_XMMS2)
1715         END OBJ(xmms2_bitrate, INFO_XMMS2)
1716         END OBJ(xmms2_date, INFO_XMMS2)
1717         END OBJ(xmms2_id, INFO_XMMS2)
1718         END OBJ(xmms2_duration, INFO_XMMS2)
1719         END OBJ(xmms2_elapsed, INFO_XMMS2)
1720         END OBJ(xmms2_size, INFO_XMMS2)
1721         END OBJ(xmms2_status, INFO_XMMS2)
1722         END OBJ(xmms2_percent, INFO_XMMS2)
1723 #ifdef X11
1724         END OBJ(xmms2_bar, INFO_XMMS2)
1725                 SIZE_DEFAULTS(bar);
1726                 scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
1727 #endif /* X11 */
1728         END OBJ(xmms2_smart, INFO_XMMS2)
1729         END OBJ(xmms2_playlist, INFO_XMMS2)
1730         END OBJ(xmms2_timesplayed, INFO_XMMS2)
1731         END OBJ_IF(if_xmms2_connected, INFO_XMMS2)
1732 #endif
1733 #ifdef AUDACIOUS
1734         END OBJ(audacious_status, INFO_AUDACIOUS)
1735         END OBJ(audacious_title, INFO_AUDACIOUS)
1736                 if (arg) {
1737                         sscanf(arg, "%d", &info.audacious.max_title_len);
1738                         if (info.audacious.max_title_len > 0) {
1739                                 info.audacious.max_title_len++;
1740                         } else {
1741                                 CRIT_ERR(obj, free_at_crash, "audacious_title: invalid length argument");
1742                         }
1743                 }
1744         END OBJ(audacious_length, INFO_AUDACIOUS)
1745         END OBJ(audacious_length_seconds, INFO_AUDACIOUS)
1746         END OBJ(audacious_position, INFO_AUDACIOUS)
1747         END OBJ(audacious_position_seconds, INFO_AUDACIOUS)
1748         END OBJ(audacious_bitrate, INFO_AUDACIOUS)
1749         END OBJ(audacious_frequency, INFO_AUDACIOUS)
1750         END OBJ(audacious_channels, INFO_AUDACIOUS)
1751         END OBJ(audacious_filename, INFO_AUDACIOUS)
1752         END OBJ(audacious_playlist_length, INFO_AUDACIOUS)
1753         END OBJ(audacious_playlist_position, INFO_AUDACIOUS)
1754         END OBJ(audacious_main_volume, INFO_AUDACIOUS)
1755 #ifdef X11
1756         END OBJ(audacious_bar, INFO_AUDACIOUS)
1757                 SIZE_DEFAULTS(bar);
1758                 scan_bar(arg, &obj->a, &obj->b);
1759 #endif /* X11 */
1760 #endif
1761 #ifdef BMPX
1762         END OBJ(bmpx_title, INFO_BMPX)
1763                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
1764         END OBJ(bmpx_artist, INFO_BMPX)
1765                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
1766         END OBJ(bmpx_album, INFO_BMPX)
1767                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
1768         END OBJ(bmpx_track, INFO_BMPX)
1769                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
1770         END OBJ(bmpx_uri, INFO_BMPX)
1771                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
1772         END OBJ(bmpx_bitrate, INFO_BMPX)
1773                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
1774 #endif
1775 #ifdef EVE
1776         END OBJ(eve, 0)
1777                 if(arg) {
1778                         int argc;
1779                         char *userid = (char *) malloc(20 * sizeof(char));
1780                         char *apikey = (char *) malloc(64 * sizeof(char));
1781                         char *charid = (char *) malloc(20 * sizeof(char));
1782
1783                         argc = sscanf(arg, "%20s %64s %20s", userid, apikey, charid);
1784                         obj->data.eve.charid = charid;
1785                         obj->data.eve.userid = userid;
1786                         obj->data.eve.apikey = apikey;
1787
1788                         init_eve();
1789                 } else {
1790                         CRIT_ERR(obj, free_at_crash, "eve needs arguments: <userid> <apikey> <characterid>");
1791                 }
1792 #endif
1793 #ifdef HAVE_CURL
1794         END OBJ(curl, 0)
1795                 if (arg) {
1796                         int argc;
1797                         float interval = 0;
1798                         char *uri = (char *) malloc(128 * sizeof(char));
1799
1800                         argc = sscanf(arg, "%127s %f", uri, &interval);
1801                         if (argc == 2) {
1802                                 obj->data.curl.uri = uri;
1803                                 obj->data.curl.interval = interval > 0 ? interval * 60 : 15*60;
1804                         } else {
1805                                 NORM_ERR("wrong number of arguments for $curl");
1806                         }
1807                 } else {
1808                         CRIT_ERR(obj, free_at_crash, "curl needs arguments: <uri> <interval in minutes>");
1809                 }
1810 #endif
1811 #ifdef RSS
1812         END OBJ(rss, 0)
1813                 if (arg) {
1814                         float interval = 0;
1815                         int argc, act_par = 0;
1816                         unsigned int nrspaces = 0;
1817                         char *uri = (char *) malloc(128 * sizeof(char));
1818                         char *action = (char *) malloc(64 * sizeof(char));
1819
1820                         argc = sscanf(arg, "%127s %f %63s %d %u", uri, &interval, action,
1821                                         &act_par, &nrspaces);
1822                         if (argc >= 3) {
1823                                 obj->data.rss.uri = uri;
1824                                 obj->data.rss.interval = interval > 0 ? interval * 60 : 15*60;
1825                                 obj->data.rss.action = action;
1826                                 obj->data.rss.act_par = act_par;
1827                                 obj->data.rss.nrspaces = nrspaces;
1828                         } else {
1829                                 NORM_ERR("wrong number of arguments for $rss");
1830                         }
1831                 } else {
1832                         CRIT_ERR(obj, free_at_crash, "rss needs arguments: <uri> <interval in minutes> <action> "
1833                                         "[act_par] [spaces in front]");
1834                 }
1835 #endif
1836 #ifdef WEATHER
1837         END OBJ(weather, 0)
1838                 if (arg) {
1839                         int argc;
1840                         float interval = 0;
1841                         char *locID = (char *) malloc(9 * sizeof(char));
1842                         char *uri = (char *) malloc(128 * sizeof(char));
1843                         char *data_type = (char *) malloc(32 * sizeof(char));
1844
1845                         argc = sscanf(arg, "%119s %8s %31s %f", uri, locID, data_type, &interval);
1846
1847                         if (argc >= 3) {
1848                                 if (process_weather_uri(uri, locID, 0)) {
1849                                         free(data_type);
1850                                         free(uri);
1851                                         free(locID);
1852                                         CRIT_ERR(obj, free_at_crash, \
1853                                                         "could not recognize the weather uri");
1854                                 }
1855
1856                                 obj->data.weather.uri = uri;
1857                                 obj->data.weather.data_type = data_type;
1858
1859                                 /* Limit the data retrieval interval to half hour min */
1860                                 if (interval < 30) {
1861                                         interval = 30;
1862                                 }
1863
1864                                 /* Convert to seconds */
1865                                 obj->data.weather.interval = interval * 60;
1866                                 free(locID);
1867
1868                                 DBGP("weather: fetching %s from %s every %d seconds", \
1869                                                 data_type, uri, obj->data.weather.interval);
1870                         } else {
1871                                 free(data_type);
1872                                 free(uri);
1873                                 free(locID);
1874                                 CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather");
1875                         }
1876                 } else {
1877                         CRIT_ERR(obj, free_at_crash, "weather needs arguments: <uri> <locID> <data_type> [interval in minutes]");
1878                 }
1879 #endif
1880 #ifdef XOAP
1881         END OBJ(weather_forecast, 0)
1882                 if (arg) {
1883                         int argc;
1884                         unsigned int day;
1885                         float interval = 0;
1886                         char *locID = (char *) malloc(9 * sizeof(char));
1887                         char *uri = (char *) malloc(128 * sizeof(char));
1888                         char *data_type = (char *) malloc(32 * sizeof(char));
1889
1890                         argc = sscanf(arg, "%119s %8s %1u %31s %f", uri, locID, &day, data_type, &interval);
1891
1892                         if (argc >= 4) {
1893                                 if (process_weather_uri(uri, locID, 1)) {
1894                                         free(data_type);
1895                                         free(uri);
1896                                         free(locID);
1897                                         CRIT_ERR(obj, free_at_crash, \
1898                                                         "could not recognize the weather forecast uri");
1899                                 }
1900
1901                                 obj->data.weather_forecast.uri = uri;
1902                                 obj->data.weather_forecast.data_type = data_type;
1903
1904                                 /* Limit the day between 0 (today) and FORECAST_DAYS */
1905                                 if (day >= FORECAST_DAYS) {
1906                                         day = FORECAST_DAYS-1;
1907                                 }
1908                                 obj->data.weather_forecast.day = day;
1909
1910                                 /* Limit the data retrieval interval to 3 hours and an half */
1911                                 if (interval < 210) {
1912                                         interval = 210;
1913                                 }
1914
1915                                 /* Convert to seconds */
1916                                 obj->data.weather_forecast.interval = interval * 60;
1917                                 free(locID);
1918
1919                                 DBGP("weather_forecast: fetching %s for day %d from %s every %d seconds", \
1920                                          data_type, day, uri, obj->data.weather_forecast.interval);
1921                         } else {
1922                                 free(data_type);
1923                                 free(uri);
1924                                 free(locID);
1925                                 CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather_forecast");
1926                         }
1927                 } else {
1928                         CRIT_ERR(obj, free_at_crash, "weather_forecast needs arguments: <uri> <locID> <day> <data_type> [interval in minutes]");
1929                 }
1930 #endif
1931 #ifdef HAVE_LUA
1932         END OBJ(lua, 0)
1933                 if (arg) {
1934                         obj->data.s = strndup(arg, text_buffer_size);
1935                 } else {
1936                         CRIT_ERR(obj, free_at_crash, "lua needs arguments: <function name> [function parameters]");
1937                 }
1938         END OBJ(lua_parse, 0)
1939                 if (arg) {
1940                         obj->data.s = strndup(arg, text_buffer_size);
1941                 } else {
1942                         CRIT_ERR(obj, free_at_crash, "lua_parse needs arguments: <function name> [function parameters]");
1943                 }
1944         END OBJ(lua_bar, 0)
1945                 SIZE_DEFAULTS(bar);
1946                 if (arg) {
1947                         arg = scan_bar(arg, &obj->a, &obj->b);
1948                         if(arg) {
1949                                 obj->data.s = strndup(arg, text_buffer_size);
1950                         } else {
1951                                 CRIT_ERR(obj, free_at_crash, "lua_bar needs arguments: <height>,<width> <function name> [function parameters]");
1952                         }
1953                 } else {
1954                         CRIT_ERR(obj, free_at_crash, "lua_bar needs arguments: <height>,<width> <function name> [function parameters]");
1955                 }
1956 #ifdef X11
1957         END OBJ(lua_graph, 0)
1958                 SIZE_DEFAULTS(graph);
1959                 if (arg) {
1960                         char *buf = 0;
1961                         buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
1962                                         &obj->e, &obj->char_a, &obj->char_b);
1963                         if (buf) {
1964                                 obj->data.s = buf;
1965                         } else {
1966                                 CRIT_ERR(obj, free_at_crash, "lua_graph needs arguments: <function name> [height],[width] [gradient colour 1] [gradient colour 2] [scale] [-t] [-l]");
1967                         }
1968                 } else {
1969                         CRIT_ERR(obj, free_at_crash, "lua_graph needs arguments: <function name> [height],[width] [gradient colour 1] [gradient colour 2] [scale] [-t] [-l]");
1970         }
1971         END OBJ(lua_gauge, 0)
1972                 SIZE_DEFAULTS(gauge);
1973                 if (arg) {
1974                         arg = scan_gauge(arg, &obj->a, &obj->b);
1975                         if (arg) {
1976                                 obj->data.s = strndup(arg, text_buffer_size);
1977                         } else {
1978                                 CRIT_ERR(obj, free_at_crash, "lua_gauge needs arguments: <height>,<width> <function name> [function parameters]");
1979                         }
1980                 } else {
1981                         CRIT_ERR(obj, free_at_crash, "lua_gauge needs arguments: <height>,<width> <function name> [function parameters]");
1982                 }
1983 #endif /* X11 */
1984 #endif /* HAVE_LUA */
1985 #ifdef HDDTEMP
1986         END OBJ(hddtemp, 0)
1987                 if (scan_hddtemp(arg, &obj->data.hddtemp.dev,
1988                                  &obj->data.hddtemp.addr, &obj->data.hddtemp.port)) {
1989                         NORM_ERR("hddtemp needs arguments");
1990                         obj->type = OBJ_text;
1991                         obj->data.s = strndup("${hddtemp}", text_buffer_size);
1992                         obj->data.hddtemp.update_time = 0;
1993                 } else
1994                         obj->data.hddtemp.temp = NULL;
1995 #endif /* HDDTEMP */
1996 #ifdef TCP_PORT_MONITOR
1997         END OBJ(tcp_portmon, INFO_TCP_PORT_MONITOR)
1998                 tcp_portmon_init(arg, &obj->data.tcp_port_monitor);
1999 #endif /* TCP_PORT_MONITOR */
2000         END OBJ(entropy_avail, INFO_ENTROPY)
2001         END OBJ(entropy_perc, INFO_ENTROPY)
2002         END OBJ(entropy_poolsize, INFO_ENTROPY)
2003         END OBJ(entropy_bar, INFO_ENTROPY)
2004                 SIZE_DEFAULTS(bar);
2005                 scan_bar(arg, &obj->a, &obj->b);
2006         END OBJ(include, 0)
2007                 if(arg) {
2008                         struct conftree *leaf = conftree_add(currentconffile, arg);
2009                         if(leaf) {
2010                                 if (load_config_file(arg) == TRUE) {
2011                                         obj->sub = malloc(sizeof(struct text_object));
2012                                         currentconffile = leaf;
2013                                         extract_variable_text_internal(obj->sub, get_global_text());
2014                                         currentconffile = leaf->back;
2015                                 } else {
2016                                         NORM_ERR("Can't load configfile '%s'.", arg);
2017                                 }
2018                         } else {
2019                                 NORM_ERR("You are trying to load '%s' recursively, I'm only going to load it once to prevent an infinite loop.", arg);
2020                         }
2021                 } else {
2022                         CRIT_ERR(obj, free_at_crash, "include needs a argument");
2023                 }
2024         END OBJ(blink, 0)
2025                 if(arg) {
2026                         obj->sub = malloc(sizeof(struct text_object));
2027                         extract_variable_text_internal(obj->sub, arg);
2028                 }else{
2029                         CRIT_ERR(obj, free_at_crash, "blink needs a argument");
2030                 }
2031         END OBJ(to_bytes, 0)
2032                 if(arg) {
2033                         obj->sub = malloc(sizeof(struct text_object));
2034                         extract_variable_text_internal(obj->sub, arg);
2035                 }else{
2036                         CRIT_ERR(obj, free_at_crash, "to_bytes needs a argument");
2037                 }
2038         END OBJ(scroll, 0)
2039                 int n1 = 0, n2 = 0;
2040
2041                 obj->data.scroll.resetcolor = get_current_text_color();
2042                 obj->data.scroll.step = 1;
2043                 if (arg && sscanf(arg, "%u %n", &obj->data.scroll.show, &n1) > 0) {
2044                         sscanf(arg + n1, "%u %n", &obj->data.scroll.step, &n2);
2045                         if (*(arg + n1 + n2)) {
2046                                 n1 += n2;
2047                         } else {
2048                                 obj->data.scroll.step = 1;
2049                         }
2050                         obj->data.scroll.text = malloc(strlen(arg + n1) + obj->data.scroll.show + 1);
2051                         for(n2 = 0; (unsigned int) n2 < obj->data.scroll.show; n2++) {
2052                                 obj->data.scroll.text[n2] = ' ';
2053                         }
2054                         obj->data.scroll.text[n2] = 0;
2055                         strcat(obj->data.scroll.text, arg + n1);
2056                         obj->data.scroll.start = 0;
2057                         obj->sub = malloc(sizeof(struct text_object));
2058                         extract_variable_text_internal(obj->sub,
2059                                         obj->data.scroll.text);
2060                 } else {
2061                         CRIT_ERR(obj, free_at_crash, "scroll needs arguments: <length> [<step>] <text>");
2062                 }
2063         END OBJ(combine, 0)
2064                 if(arg) {
2065                         unsigned int i,j;
2066                         unsigned int indenting = 0;     //vars can be used as args for other vars
2067                         int startvar[2];
2068                         int endvar[2];
2069                         startvar[0] = endvar[0] = startvar[1] = endvar[1] = -1;
2070                         j=0;
2071                         for (i=0; arg[i] != 0 && j < 2; i++) {
2072                                 if(startvar[j] == -1) {
2073                                         if(arg[i] == '$') {
2074                                                 startvar[j] = i;
2075                                         }
2076                                 }else if(endvar[j] == -1) {
2077                                         if(arg[i] == '{') {
2078                                                 indenting++;
2079                                         }else if(arg[i] == '}') {
2080                                                 indenting--;
2081                                         }
2082                                         if (indenting == 0 && arg[i+1] < 48) {  //<48 has 0, $, and the most used chars not used in varnames but not { or }
2083                                                 endvar[j]=i+1;
2084                                                 j++;
2085                                         }
2086                                 }
2087                         }
2088                         if(startvar[0] >= 0 && endvar[0] >= 0 && startvar[1] >= 0 && endvar[1] >= 0) {
2089                                 obj->data.combine.left = malloc(endvar[0]-startvar[0] + 1);
2090                                 obj->data.combine.seperation = malloc(startvar[1] - endvar[0] + 1);
2091                                 obj->data.combine.right= malloc(endvar[1]-startvar[1] + 1);
2092
2093                                 strncpy(obj->data.combine.left, arg + startvar[0], endvar[0] - startvar[0]);
2094                                 obj->data.combine.left[endvar[0] - startvar[0]] = 0;
2095
2096                                 strncpy(obj->data.combine.seperation, arg + endvar[0], startvar[1] - endvar[0]);
2097                                 obj->data.combine.seperation[startvar[1] - endvar[0]] = 0;
2098
2099                                 strncpy(obj->data.combine.right, arg + startvar[1], endvar[1] - startvar[1]);
2100                                 obj->data.combine.right[endvar[1] - startvar[1]] = 0;
2101
2102                                 obj->sub = malloc(sizeof(struct text_object));
2103                                 extract_variable_text_internal(obj->sub, obj->data.combine.left);
2104                                 obj->sub->sub = malloc(sizeof(struct text_object));
2105                                 extract_variable_text_internal(obj->sub->sub, obj->data.combine.right);
2106                         } else {
2107                                 CRIT_ERR(obj, free_at_crash, "combine needs arguments: <text1> <text2>");
2108                         }
2109                 } else {
2110                         CRIT_ERR(obj, free_at_crash, "combine needs arguments: <text1> <text2>");
2111                 }
2112 #ifdef NVIDIA
2113         END OBJ(nvidia, 0)
2114                 if (!arg) {
2115                         CRIT_ERR(obj, free_at_crash, "nvidia needs an argument\n");
2116                 } else if (set_nvidia_type(&obj->data.nvidia, arg)) {
2117                         CRIT_ERR(obj, free_at_crash, "nvidia: invalid argument"
2118                                  " specified: '%s'\n", arg);
2119                 }
2120 #endif /* NVIDIA */
2121 #ifdef APCUPSD
2122                 init_apcupsd();
2123                 END OBJ(apcupsd, INFO_APCUPSD)
2124                         if (arg) {
2125                                 char host[64];
2126                                 int port;
2127                                 if (sscanf(arg, "%63s %d", host, &port) != 2) {
2128                                         CRIT_ERR(obj, free_at_crash, "apcupsd needs arguments: <host> <port>");
2129                                 } else {
2130                                         info.apcupsd.port = htons(port);
2131                                         strncpy(info.apcupsd.host, host, sizeof(info.apcupsd.host));
2132                                 }
2133                         } else {
2134                                 CRIT_ERR(obj, free_at_crash, "apcupsd needs arguments: <host> <port>");
2135                         }
2136                         END OBJ(apcupsd_name, INFO_APCUPSD)
2137                         END OBJ(apcupsd_model, INFO_APCUPSD)
2138                         END OBJ(apcupsd_upsmode, INFO_APCUPSD)
2139                         END OBJ(apcupsd_cable, INFO_APCUPSD)
2140                         END OBJ(apcupsd_status, INFO_APCUPSD)
2141                         END OBJ(apcupsd_linev, INFO_APCUPSD)
2142                         END OBJ(apcupsd_load, INFO_APCUPSD)
2143                         END OBJ(apcupsd_loadbar, INFO_APCUPSD)
2144                                 SIZE_DEFAULTS(bar);
2145                                 scan_bar(arg, &obj->a, &obj->b);
2146 #ifdef X11
2147                         END OBJ(apcupsd_loadgraph, INFO_APCUPSD)
2148                                 char* buf = 0;
2149                                 SIZE_DEFAULTS(graph);
2150                                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
2151                                                 &obj->e, &obj->char_a, &obj->char_b);
2152                                 if (buf) free(buf);
2153                         END OBJ(apcupsd_loadgauge, INFO_APCUPSD)
2154                                 SIZE_DEFAULTS(gauge);
2155                                 scan_gauge(arg, &obj->a, &obj->b);
2156 #endif /* X11 */
2157                         END OBJ(apcupsd_charge, INFO_APCUPSD)
2158                         END OBJ(apcupsd_timeleft, INFO_APCUPSD)
2159                         END OBJ(apcupsd_temp, INFO_APCUPSD)
2160                         END OBJ(apcupsd_lastxfer, INFO_APCUPSD)
2161 #endif /* APCUPSD */
2162         END {
2163                 char buf[256];
2164
2165                 NORM_ERR("unknown variable %s", s);
2166                 obj->type = OBJ_text;
2167                 snprintf(buf, 256, "${%s}", s);
2168                 obj->data.s = strndup(buf, text_buffer_size);
2169         }
2170 #undef OBJ
2171
2172         return obj;
2173 }
2174
2175 /* backslash_escape - do the actual substitution task for template objects
2176  *
2177  * The field templates is used for substituting the \N occurences. Set it to
2178  * NULL to leave them as they are.
2179  */
2180 static char *backslash_escape(const char *src, char **templates, unsigned int template_count)
2181 {
2182         char *src_dup;
2183         const char *p;
2184         unsigned int dup_idx = 0, dup_len;
2185
2186         dup_len = strlen(src) + 1;
2187         src_dup = malloc(dup_len * sizeof(char));
2188
2189         p = src;
2190         while (*p) {
2191                 switch (*p) {
2192                 case '\\':
2193                         if (!*(p + 1))
2194                                 break;
2195                         if (*(p + 1) == '\\') {
2196                                 src_dup[dup_idx++] = '\\';
2197                                 p++;
2198                         } else if (*(p + 1) == ' ') {
2199                                 src_dup[dup_idx++] = ' ';
2200                                 p++;
2201                         } else if (*(p + 1) == 'n') {
2202                                 src_dup[dup_idx++] = '\n';
2203                                 p++;
2204                         } else if (templates) {
2205                                 unsigned int tmpl_num;
2206                                 int digits;
2207                                 if ((sscanf(p + 1, "%u%n", &tmpl_num, &digits) <= 0) ||
2208                                     (tmpl_num > template_count))
2209                                         break;
2210                                 dup_len += strlen(templates[tmpl_num - 1]);
2211                                 src_dup = realloc(src_dup, dup_len * sizeof(char));
2212                                 sprintf(src_dup + dup_idx, "%s", templates[tmpl_num - 1]);
2213                                 dup_idx += strlen(templates[tmpl_num - 1]);
2214                                 p += digits;
2215                         }
2216                         break;
2217                 default:
2218                         src_dup[dup_idx++] = *p;
2219                         break;
2220                 }
2221                 p++;
2222         }
2223         src_dup[dup_idx] = '\0';
2224         src_dup = realloc(src_dup, (strlen(src_dup) + 1) * sizeof(char));
2225         return src_dup;
2226 }
2227
2228 /* handle_template_object - core logic of the template object
2229  *
2230  * use config variables like this:
2231  * template1 = "$\1\2"
2232  * template2 = "\1: ${fs_bar 4,100 \2} ${fs_used \2} / ${fs_size \2}"
2233  *
2234  * and use them like this:
2235  * ${template1 node name}
2236  * ${template2 root /}
2237  * ${template2 cdrom /mnt/cdrom}
2238  */
2239 static char *handle_template(const char *tmpl, const char *args)
2240 {
2241         char *args_dup = NULL;
2242         char *p, *p_old;
2243         char **argsp = NULL;
2244         unsigned int argcnt = 0, template_idx, i;
2245         char *eval_text;
2246
2247         if ((sscanf(tmpl, "template%u", &template_idx) != 1) ||
2248             (template_idx >= MAX_TEMPLATES))
2249                 return NULL;
2250
2251         if(args) {
2252                 args_dup = strdup(args);
2253                 p = args_dup;
2254                 while (*p) {
2255                         while (*p && (*p == ' ' && (p == args_dup || *(p - 1) != '\\')))
2256                                 p++;
2257                         if (p > args_dup && *(p - 1) == '\\')
2258                                 p--;
2259                         p_old = p;
2260                         while (*p && (*p != ' ' || (p > args_dup && *(p - 1) == '\\')))
2261                                 p++;
2262                         if (*p) {
2263                                 (*p) = '\0';
2264                                 p++;
2265                         }
2266                         argsp = realloc(argsp, ++argcnt * sizeof(char *));
2267                         argsp[argcnt - 1] = p_old;
2268                 }
2269                 for (i = 0; i < argcnt; i++) {
2270                         char *tmp;
2271                         tmp = backslash_escape(argsp[i], NULL, 0);
2272                         DBGP2("%s: substituted arg '%s' to '%s'", tmpl, argsp[i], tmp);
2273                         argsp[i] = tmp;
2274                 }
2275         }
2276
2277         eval_text = backslash_escape(get_templates()[template_idx], argsp, argcnt);
2278         DBGP("substituted %s, output is '%s'", tmpl, eval_text);
2279         free(args_dup);
2280         for (i = 0; i < argcnt; i++)
2281                 free(argsp[i]);
2282         free(argsp);
2283         return eval_text;
2284 }
2285
2286 static char *find_and_replace_templates(const char *inbuf)
2287 {
2288         char *outbuf, *indup, *p, *o, *templ, *args, *tmpl_out;
2289         int stack, outlen;
2290
2291         outlen = strlen(inbuf) + 1;
2292         o = outbuf = calloc(outlen, sizeof(char));
2293         memset(outbuf, 0, outlen * sizeof(char));
2294
2295         p = indup = strdup(inbuf);
2296         while (*p) {
2297                 while (*p && *p != '$')
2298                         *(o++) = *(p++);
2299
2300                 if (!(*p))
2301                         break;
2302
2303                 if (strncmp(p, "$template", 9) && strncmp(p, "${template", 10)) {
2304                         *(o++) = *(p++);
2305                         continue;
2306                 }
2307
2308                 if (*(p + 1) == '{') {
2309                         p += 2;
2310                         templ = p;
2311                         while (*p && !isspace(*p) && *p != '{' && *p != '}')
2312                                 p++;
2313                         if (*p == '}')
2314                                 args = NULL;
2315                         else
2316                                 args = p;
2317
2318                         stack = 1;
2319                         while (*p && stack > 0) {
2320                                 if (*p == '{')
2321                                         stack++;
2322                                 else if (*p == '}')
2323                                         stack--;
2324                                 p++;
2325                         }
2326                         if (stack == 0) {
2327                                 // stack is empty. that means the previous char was }, so we zero it
2328                                 *(p - 1) = '\0';
2329                         } else {
2330                                 // we ran into the end of string without finding a closing }, bark
2331                                 CRIT_ERR(NULL, NULL, "cannot find a closing '}' in template expansion");
2332                         }
2333                 } else {
2334                         templ = p + 1;
2335                         while (*p && !isspace(*p))
2336                                 p++;
2337                         args = NULL;
2338                 }
2339                 tmpl_out = handle_template(templ, args);
2340                 if (tmpl_out) {
2341                         outlen += strlen(tmpl_out);
2342                         *o = '\0';
2343                         outbuf = realloc(outbuf, outlen * sizeof(char));
2344                         strcat (outbuf, tmpl_out);
2345                         free(tmpl_out);
2346                         o = outbuf + strlen(outbuf);
2347                 } else {
2348                         NORM_ERR("failed to handle template '%s' with args '%s'", templ, args);
2349                 }
2350         }
2351         *o = '\0';
2352         outbuf = realloc(outbuf, (strlen(outbuf) + 1) * sizeof(char));
2353         free(indup);
2354         return outbuf;
2355 }
2356
2357 static int text_contains_templates(const char *text)
2358 {
2359         if (strcasestr(text, "${template") != NULL)
2360                 return 1;
2361         if (strcasestr(text, "$template") != NULL)
2362                 return 1;
2363         return 0;
2364 }
2365
2366 /*
2367  * - assumes that *string is '#'
2368  * - removes the part from '#' to the end of line ('\n' or '\0')
2369  * - it removes the '\n'
2370  * - copies the last char into 'char *last' argument, which should be a pointer
2371  *   to a char rather than a string.
2372  */
2373 static size_t remove_comment(char *string, char *last)
2374 {
2375         char *end = string;
2376         while (*end != '\0' && *end != '\n') {
2377                 ++end;
2378         }
2379         if (last) *last = *end;
2380         if (*end == '\n') end++;
2381         strfold(string, end - string);
2382         return end - string;
2383 }
2384
2385 size_t remove_comments(char *string)
2386 {
2387         char *curplace;
2388         size_t folded = 0;
2389         for (curplace = string; *curplace != 0; curplace++) {
2390                 if (*curplace == '\\' && *(curplace + 1) == '#') {
2391                         // strcpy can't be used for overlapping strings
2392                         strfold(curplace, 1);
2393                         folded += 1;
2394                 } else if (*curplace == '#') {
2395                         folded += remove_comment(curplace, 0);
2396                 }
2397         }
2398         return folded;
2399 }
2400
2401 int extract_variable_text_internal(struct text_object *retval, const char *const_p)
2402 {
2403         struct text_object *obj;
2404         char *p, *s, *orig_p;
2405         long line;
2406         void *ifblock_opaque = NULL;
2407         char *tmp_p;
2408         char *arg = 0;
2409         size_t len = 0;
2410
2411         p = strndup(const_p, max_user_text - 1);
2412         while (text_contains_templates(p)) {
2413                 char *tmp;
2414                 tmp = find_and_replace_templates(p);
2415                 free(p);
2416                 p = tmp;
2417         }
2418         s = orig_p = p;
2419
2420         if (strcmp(p, const_p)) {
2421                 DBGP("replaced all templates in text: input is\n'%s'\noutput is\n'%s'", const_p, p);
2422         } else {
2423                 DBGP("no templates to replace");
2424         }
2425
2426         memset(retval, 0, sizeof(struct text_object));
2427
2428         line = global_text_lines;
2429
2430         while (*p) {
2431                 if (*p == '\n') {
2432                         line++;
2433                 }
2434                 if (*p == '$') {
2435                         *p = '\0';
2436                         obj = create_plain_text(s);
2437                         if (obj != NULL) {
2438                                 append_object(retval, obj);
2439                         }
2440                         *p = '$';
2441                         p++;
2442                         s = p;
2443
2444                         if (*p != '$') {
2445                                 char buf[256];
2446                                 const char *var;
2447
2448                                 /* variable is either $foo or ${foo} */
2449                                 if (*p == '{') {
2450                                         unsigned int brl = 1, brr = 0;
2451
2452                                         p++;
2453                                         s = p;
2454                                         while (*p && brl != brr) {
2455                                                 if (*p == '{') {
2456                                                         brl++;
2457                                                 }
2458                                                 if (*p == '}') {
2459                                                         brr++;
2460                                                 }
2461                                                 p++;
2462                                         }
2463                                         p--;
2464                                 } else {
2465                                         s = p;
2466                                         if (*p == '#') {
2467                                                 p++;
2468                                         }
2469                                         while (*p && (isalnum((int) *p) || *p == '_')) {
2470                                                 p++;
2471                                         }
2472                                 }
2473
2474                                 /* copy variable to buffer */
2475                                 len = (p - s > 255) ? 255 : (p - s);
2476                                 strncpy(buf, s, len);
2477                                 buf[len] = '\0';
2478
2479                                 if (*p == '}') {
2480                                         p++;
2481                                 }
2482                                 s = p;
2483
2484                                 /* search for variable in environment */
2485
2486                                 var = getenv(buf);
2487                                 if (var) {
2488                                         obj = create_plain_text(var);
2489                                         if (obj) {
2490                                                 append_object(retval, obj);
2491                                         }
2492                                         continue;
2493                                 }
2494
2495                                 /* if variable wasn't found in environment, use some special */
2496
2497                                 arg = 0;
2498
2499                                 /* split arg */
2500                                 if (strchr(buf, ' ')) {
2501                                         arg = strchr(buf, ' ');
2502                                         *arg = '\0';
2503                                         arg++;
2504                                         while (isspace((int) *arg)) {
2505                                                 arg++;
2506                                         }
2507                                         if (!*arg) {
2508                                                 arg = 0;
2509                                         }
2510                                 }
2511
2512                                 /* lowercase variable name */
2513                                 tmp_p = buf;
2514                                 while (*tmp_p) {
2515                                         *tmp_p = tolower(*tmp_p);
2516                                         tmp_p++;
2517                                 }
2518
2519                                 obj = construct_text_object(buf, arg,
2520                                                 line, &ifblock_opaque, orig_p);
2521                                 if (obj != NULL) {
2522                                         append_object(retval, obj);
2523                                 }
2524                                 continue;
2525                         } else {
2526                                 obj = create_plain_text("$");
2527                                 s = p + 1;
2528                                 if (obj != NULL) {
2529                                         append_object(retval, obj);
2530                                 }
2531                         }
2532                 } else if (*p == '\\' && *(p+1) == '#') {
2533                         strfold(p, 1);
2534                 } else if (*p == '#') {
2535                         char c;
2536                         if (remove_comment(p, &c) && p > orig_p && c == '\n') {
2537                                 /* if remove_comment removed a newline, we need to 'back up' with p */
2538                                 p--;
2539                         }
2540                 }
2541                 p++;
2542         }
2543         obj = create_plain_text(s);
2544         if (obj != NULL) {
2545                 append_object(retval, obj);
2546         }
2547
2548         if (!ifblock_stack_empty(&ifblock_opaque)) {
2549                 NORM_ERR("one or more $endif's are missing");
2550         }
2551
2552         free(orig_p);
2553         return 0;
2554 }
2555
2556 /*
2557  * Frees the list of text objects root points to.  When internal = 1, it won't
2558  * free global objects.
2559  */
2560 void free_text_objects(struct text_object *root, int internal)
2561 {
2562         struct text_object *obj;
2563
2564         if (!root->prev) {
2565                 return;
2566         }
2567
2568 #define data obj->data
2569         for (obj = root->prev; obj; obj = root->prev) {
2570                 root->prev = obj->prev;
2571                 switch (obj->type) {
2572 #ifndef __OpenBSD__
2573                         case OBJ_acpitemp:
2574                                 close(data.i);
2575                                 break;
2576 #endif /* !__OpenBSD__ */
2577 #ifdef __linux__
2578                         case OBJ_i2c:
2579                         case OBJ_platform:
2580                         case OBJ_hwmon:
2581                                 close(data.sysfs.fd);
2582                                 break;
2583 #endif /* __linux__ */
2584                         case OBJ_read_tcp:
2585                                 free(data.read_tcp.host);
2586                                 break;
2587                         case OBJ_time:
2588                         case OBJ_utime:
2589                                 free(data.s);
2590                                 break;
2591                         case OBJ_tztime:
2592                                 free(data.tztime.tz);
2593                                 free(data.tztime.fmt);
2594                                 break;
2595                         case OBJ_mboxscan:
2596                                 free(data.mboxscan.args);
2597                                 free(data.mboxscan.output);
2598                                 break;
2599                         case OBJ_mails:
2600                         case OBJ_new_mails:
2601                         case OBJ_seen_mails:
2602                         case OBJ_unseen_mails:
2603                         case OBJ_flagged_mails:
2604                         case OBJ_unflagged_mails:
2605                         case OBJ_forwarded_mails:
2606                         case OBJ_unforwarded_mails:
2607                         case OBJ_replied_mails:
2608                         case OBJ_unreplied_mails:
2609                         case OBJ_draft_mails:
2610                         case OBJ_trashed_mails:
2611                                 free(data.local_mail.mbox);
2612                                 break;
2613                         case OBJ_imap_unseen:
2614                                 if (!obj->char_b) {
2615                                         free(data.mail);
2616                                 }
2617                                 break;
2618                         case OBJ_imap_messages:
2619                                 if (!obj->char_b) {
2620                                         free(data.mail);
2621                                 }
2622                                 break;
2623                         case OBJ_pop3_unseen:
2624                                 if (!obj->char_b) {
2625                                         free(data.mail);
2626                                 }
2627                                 break;
2628                         case OBJ_pop3_used:
2629                                 if (!obj->char_b) {
2630                                         free(data.mail);
2631                                 }
2632                                 break;
2633                         case OBJ_if_empty:
2634                         case OBJ_if_match:
2635                                 free_text_objects(obj->sub, 1);
2636                                 free(obj->sub);
2637                                 /* fall through */
2638                         case OBJ_if_existing:
2639                         case OBJ_if_mounted:
2640                         case OBJ_if_running:
2641                                 free(data.ifblock.s);
2642                                 free(data.ifblock.str);
2643                                 break;
2644                         case OBJ_head:
2645                         case OBJ_tail:
2646                                 free(data.headtail.logfile);
2647                                 if(data.headtail.buffer) {
2648                                         free(data.headtail.buffer);
2649                                 }
2650                                 break;
2651                         case OBJ_text:
2652                         case OBJ_font:
2653                         case OBJ_image:
2654                         case OBJ_eval:
2655                         case OBJ_exec:
2656                         case OBJ_execbar:
2657 #ifdef X11
2658                         case OBJ_execgauge:
2659                         case OBJ_execgraph:
2660 #endif
2661                         case OBJ_execp:
2662                                 free(data.s);
2663                                 break;
2664 #ifdef HAVE_ICONV
2665                         case OBJ_iconv_start:
2666                                 free_iconv();
2667                                 break;
2668 #endif
2669 #ifdef __linux__
2670                         case OBJ_disk_protect:
2671                                 free(data.s);
2672                                 break;
2673                         case OBJ_if_gw:
2674                                 free(data.ifblock.s);
2675                                 free(data.ifblock.str);
2676                         case OBJ_gw_iface:
2677                         case OBJ_gw_ip:
2678                                 if (info.gw_info.iface) {
2679                                         free(info.gw_info.iface);
2680                                         info.gw_info.iface = 0;
2681                                 }
2682                                 if (info.gw_info.ip) {
2683                                         free(info.gw_info.ip);
2684                                         info.gw_info.ip = 0;
2685                                 }
2686                                 break;
2687                         case OBJ_ioscheduler:
2688                                 if(data.s)
2689                                         free(data.s);
2690                                 break;
2691 #endif
2692 #if (defined(__FreeBSD__) || defined(__linux__))
2693                         case OBJ_if_up:
2694                                 free(data.ifblock.s);
2695                                 free(data.ifblock.str);
2696                                 break;
2697 #endif
2698 #ifdef XMMS2
2699                         case OBJ_xmms2_artist:
2700                                 if (info.xmms2.artist) {
2701                                         free(info.xmms2.artist);
2702                                         info.xmms2.artist = 0;
2703                                 }
2704                                 break;
2705                         case OBJ_xmms2_album:
2706                                 if (info.xmms2.album) {
2707                                         free(info.xmms2.album);
2708                                         info.xmms2.album = 0;
2709                                 }
2710                                 break;
2711                         case OBJ_xmms2_title:
2712                                 if (info.xmms2.title) {
2713                                         free(info.xmms2.title);
2714                                         info.xmms2.title = 0;
2715                                 }
2716                                 break;
2717                         case OBJ_xmms2_genre:
2718                                 if (info.xmms2.genre) {
2719                                         free(info.xmms2.genre);
2720                                         info.xmms2.genre = 0;
2721                                 }
2722                                 break;
2723                         case OBJ_xmms2_comment:
2724                                 if (info.xmms2.comment) {
2725                                         free(info.xmms2.comment);
2726                                         info.xmms2.comment = 0;
2727                                 }
2728                                 break;
2729                         case OBJ_xmms2_url:
2730                                 if (info.xmms2.url) {
2731                                         free(info.xmms2.url);
2732                                         info.xmms2.url = 0;
2733                                 }
2734                                 break;
2735                         case OBJ_xmms2_date:
2736                                 if (info.xmms2.date) {
2737                                         free(info.xmms2.date);
2738                                         info.xmms2.date = 0;
2739                                 }
2740                                 break;
2741                         case OBJ_xmms2_status:
2742                                 if (info.xmms2.status) {
2743                                         free(info.xmms2.status);
2744                                         info.xmms2.status = 0;
2745                                 }
2746                                 break;
2747                         case OBJ_xmms2_playlist:
2748                                 if (info.xmms2.playlist) {
2749                                         free(info.xmms2.playlist);
2750                                         info.xmms2.playlist = 0;
2751                                 }
2752                                 break;
2753                         case OBJ_xmms2_smart:
2754                                 if (info.xmms2.artist) {
2755                                         free(info.xmms2.artist);
2756                                         info.xmms2.artist = 0;
2757                                 }
2758                                 if (info.xmms2.title) {
2759                                         free(info.xmms2.title);
2760                                         info.xmms2.title = 0;
2761                                 }
2762                                 if (info.xmms2.url) {
2763                                         free(info.xmms2.url);
2764                                         info.xmms2.url = 0;
2765                                 }
2766                                 break;
2767 #endif
2768 #ifdef BMPX
2769                         case OBJ_bmpx_title:
2770                         case OBJ_bmpx_artist:
2771                         case OBJ_bmpx_album:
2772                         case OBJ_bmpx_track:
2773                         case OBJ_bmpx_uri:
2774                         case OBJ_bmpx_bitrate:
2775                                 break;
2776 #endif
2777 #ifdef EVE
2778                         case OBJ_eve:
2779                                 break;
2780 #endif
2781 #ifdef HAVE_CURL
2782                         case OBJ_curl:
2783                                 free(data.curl.uri);
2784                                 break;
2785 #endif
2786 #ifdef RSS
2787                         case OBJ_rss:
2788                                 free(data.rss.uri);
2789                                 free(data.rss.action);
2790                                 break;
2791 #endif
2792 #ifdef WEATHER
2793                         case OBJ_weather:
2794                                 free(data.weather.uri);
2795                                 free(data.weather.data_type);
2796                                 break;
2797 #endif
2798 #ifdef XOAP
2799                         case OBJ_weather_forecast:
2800                                 free(data.weather_forecast.uri);
2801                                 free(data.weather_forecast.data_type);
2802                                 break;
2803 #endif
2804 #ifdef HAVE_LUA
2805                         case OBJ_lua:
2806                         case OBJ_lua_parse:
2807                         case OBJ_lua_bar:
2808 #ifdef X11
2809                         case OBJ_lua_graph:
2810                         case OBJ_lua_gauge:
2811 #endif /* X11 */
2812                                 free(data.s);
2813                                 break;
2814 #endif /* HAVE_LUA */
2815                         case OBJ_pre_exec:
2816                                 break;
2817 #ifndef __OpenBSD__
2818                         case OBJ_battery:
2819                                 free(data.s);
2820                                 break;
2821                         case OBJ_battery_short:
2822                                 free(data.s);
2823                                 break;
2824                         case OBJ_battery_time:
2825                                 free(data.s);
2826                                 break;
2827 #endif /* !__OpenBSD__ */
2828                         case OBJ_execpi:
2829                         case OBJ_execi:
2830                         case OBJ_execibar:
2831 #ifdef X11
2832                         case OBJ_execigraph:
2833                         case OBJ_execigauge:
2834 #endif /* X11 */
2835                                 free(data.execi.cmd);
2836                                 free(data.execi.buffer);
2837                                 break;
2838                         case OBJ_texeci:
2839                                 if (data.texeci.p_timed_thread) timed_thread_destroy(data.texeci.p_timed_thread, &data.texeci.p_timed_thread);
2840                                 free(data.texeci.cmd);
2841                                 free(data.texeci.buffer);
2842                                 break;
2843                         case OBJ_nameserver:
2844                                 free_dns_data();
2845                                 break;
2846                         case OBJ_top:
2847                         case OBJ_top_mem:
2848                         case OBJ_top_time:
2849 #ifdef IOSTATS
2850                         case OBJ_top_io:
2851 #endif
2852                                 if (info.first_process && !internal) {
2853                                         free_all_processes();
2854                                         info.first_process = NULL;
2855                                 }
2856                                 if (data.top.s) free(data.top.s);
2857                                 break;
2858 #ifdef HDDTEMP
2859                         case OBJ_hddtemp:
2860                                 free(data.hddtemp.dev);
2861                                 free(data.hddtemp.addr);
2862                                 if (data.hddtemp.temp)
2863                                         free(data.hddtemp.temp);
2864                                 break;
2865 #endif /* HDDTEMP */
2866                         case OBJ_entropy_avail:
2867                         case OBJ_entropy_perc:
2868                         case OBJ_entropy_poolsize:
2869                         case OBJ_entropy_bar:
2870                                 break;
2871                         case OBJ_user_names:
2872                                 if (info.users.names) {
2873                                         free(info.users.names);
2874                                         info.users.names = 0;
2875                                 }
2876                                 break;
2877                         case OBJ_user_terms:
2878                                 if (info.users.terms) {
2879                                         free(info.users.terms);
2880                                         info.users.terms = 0;
2881                                 }
2882                                 break;
2883                         case OBJ_user_times:
2884                                 if (info.users.times) {
2885                                         free(info.users.times);
2886                                         info.users.times = 0;
2887                                 }
2888                                 break;
2889 #ifdef IBM
2890                         case OBJ_smapi:
2891                         case OBJ_smapi_bat_perc:
2892                         case OBJ_smapi_bat_temp:
2893                         case OBJ_smapi_bat_power:
2894                                 free(data.s);
2895                                 break;
2896                         case OBJ_if_smapi_bat_installed:
2897                                 free(data.ifblock.s);
2898                                 free(data.ifblock.str);
2899                                 break;
2900 #endif /* IBM */
2901 #ifdef NVIDIA
2902                         case OBJ_nvidia:
2903                                 break;
2904 #endif /* NVIDIA */
2905 #ifdef MPD
2906                         case OBJ_mpd_title:
2907                         case OBJ_mpd_artist:
2908                         case OBJ_mpd_album:
2909                         case OBJ_mpd_random:
2910                         case OBJ_mpd_repeat:
2911                         case OBJ_mpd_vol:
2912                         case OBJ_mpd_bitrate:
2913                         case OBJ_mpd_status:
2914                         case OBJ_mpd_bar:
2915                         case OBJ_mpd_elapsed:
2916                         case OBJ_mpd_length:
2917                         case OBJ_mpd_track:
2918                         case OBJ_mpd_name:
2919                         case OBJ_mpd_file:
2920                         case OBJ_mpd_percent:
2921                         case OBJ_mpd_smart:
2922                         case OBJ_if_mpd_playing:
2923                                 free_mpd();
2924                                 break;
2925 #endif /* MPD */
2926 #ifdef MOC
2927                         case OBJ_moc_state:
2928                         case OBJ_moc_file:
2929                         case OBJ_moc_title:
2930                         case OBJ_moc_artist:
2931                         case OBJ_moc_song:
2932                         case OBJ_moc_album:
2933                         case OBJ_moc_totaltime:
2934                         case OBJ_moc_timeleft:
2935                         case OBJ_moc_curtime:
2936                         case OBJ_moc_bitrate:
2937                         case OBJ_moc_rate:
2938                                 free_moc();
2939                                 break;
2940 #endif /* MOC */
2941                         case OBJ_include:
2942                         case OBJ_blink:
2943                         case OBJ_to_bytes:
2944                                 if(obj->sub) {
2945                                         free_text_objects(obj->sub, 1);
2946                                         free(obj->sub);
2947                                 }
2948                                 break;
2949                         case OBJ_scroll:
2950                                 free(data.scroll.text);
2951                                 free_text_objects(obj->sub, 1);
2952                                 free(obj->sub);
2953                                 break;
2954                         case OBJ_combine:
2955                                 free(data.combine.left);
2956                                 free(data.combine.seperation);
2957                                 free(data.combine.right);
2958                                 free_text_objects(obj->sub, 1);
2959                                 free(obj->sub);
2960                                 break;
2961 #ifdef APCUPSD
2962                         case OBJ_apcupsd:
2963                         case OBJ_apcupsd_name:
2964                         case OBJ_apcupsd_model:
2965                         case OBJ_apcupsd_upsmode:
2966                         case OBJ_apcupsd_cable:
2967                         case OBJ_apcupsd_status:
2968                         case OBJ_apcupsd_linev:
2969                         case OBJ_apcupsd_load:
2970                         case OBJ_apcupsd_loadbar:
2971 #ifdef X11
2972                         case OBJ_apcupsd_loadgraph:
2973                         case OBJ_apcupsd_loadgauge:
2974 #endif /* X11 */
2975                         case OBJ_apcupsd_charge:
2976                         case OBJ_apcupsd_timeleft:
2977                         case OBJ_apcupsd_temp:
2978                         case OBJ_apcupsd_lastxfer:
2979                                 break;
2980 #endif /* APCUPSD */
2981 #ifdef X11
2982                         case OBJ_desktop:
2983                         case OBJ_desktop_number:
2984                         case OBJ_desktop_name:
2985                                 if(info.x11.desktop.name) {
2986                                   free(info.x11.desktop.name);
2987                                   info.x11.desktop.name = NULL;
2988                                 }
2989                                 if(info.x11.desktop.all_names) {
2990                                   free(info.x11.desktop.all_names);
2991                                   info.x11.desktop.all_names = NULL;
2992                                 }
2993                                 break;
2994 #endif /* X11 */
2995                 }
2996                 free(obj);
2997         }
2998 #undef data
2999 }
3000