bb18ab5e758eaa414d9d375a5a7ce4c98f9ca318
[monky] / src / conky.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
11  *      (see AUTHORS)
12  * All rights reserved.
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * You should have received a copy of the GNU General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  *
26  */
27
28 #include "config.h"
29 #include "text_object.h"
30 #include "conky.h"
31 #include "common.h"
32 #include <stdarg.h>
33 #include <math.h>
34 #include <ctype.h>
35 #include <time.h>
36 #include <locale.h>
37 #include <signal.h>
38 #include <errno.h>
39 #include <limits.h>
40 #if HAVE_DIRENT_H
41 #include <dirent.h>
42 #endif
43 #include <sys/time.h>
44 #include <sys/param.h>
45 #ifdef HAVE_SYS_INOTIFY_H
46 #include <sys/inotify.h>
47 #endif /* HAVE_SYS_INOTIFY_H */
48 #ifdef X11
49 #include "x11.h"
50 #include <X11/Xutil.h>
51 #ifdef HAVE_XDAMAGE
52 #include <X11/extensions/Xdamage.h>
53 #endif
54 #ifdef IMLIB2
55 #include "imlib2.h"
56 #endif /* IMLIB2 */
57 #endif /* X11 */
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <netinet/in.h>
61 #include <netdb.h>
62 #include <fcntl.h>
63 #include <getopt.h>
64
65 /* local headers */
66 #include "algebra.h"
67 #include "build.h"
68 #include "colours.h"
69 #include "diskio.h"
70 #ifdef X11
71 #include "fonts.h"
72 #endif
73 #include "fs.h"
74 #include "logging.h"
75 #include "mixer.h"
76 #include "mail.h"
77 #include "mboxscan.h"
78 #include "specials.h"
79 #include "temphelper.h"
80 #include "tailhead.h"
81 #include "top.h"
82
83 /* check for OS and include appropriate headers */
84 #if defined(__linux__)
85 #include "linux.h"
86 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
87 #include "freebsd.h"
88 #elif defined(__OpenBSD__)
89 #include "openbsd.h"
90 #endif
91
92 #if defined(__FreeBSD_kernel__)
93 #include <bsd/bsd.h>
94 #endif
95
96 /* FIXME: apm_getinfo is unused here. maybe it's meant for common.c */
97 #if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \
98                 || defined(__OpenBSD__)) && (defined(i386) || defined(__i386__))
99 int apm_getinfo(int fd, apm_info_t aip);
100 char *get_apm_adapter(void);
101 char *get_apm_battery_life(void);
102 char *get_apm_battery_time(void);
103 #endif
104
105 #ifdef HAVE_ICONV
106 #include <iconv.h>
107 #endif
108
109 #ifdef CONFIG_OUTPUT
110 #include "defconfig.h"
111 #ifdef HAVE_FOPENCOOKIE
112 #include "conf_cookie.h"
113 #endif
114 #endif
115
116 #ifndef S_ISSOCK
117 #define S_ISSOCK(x)   ((x & S_IFMT) == S_IFSOCK)
118 #endif
119
120 #define MAIL_FILE "$MAIL"
121 #define MAX_IF_BLOCK_DEPTH 5
122
123 //#define SIGNAL_BLOCKING
124 #undef SIGNAL_BLOCKING
125
126 /* debugging level, used by logging.h */
127 int global_debug_level = 0;
128
129 /* two strings for internal use */
130 static char *tmpstring1, *tmpstring2;
131
132 /* variables holding various config settings */
133 int short_units;
134 int cpu_separate;
135 enum {
136         NO_SPACER = 0,
137         LEFT_SPACER,
138         RIGHT_SPACER
139 } use_spacer;
140 int top_cpu, top_mem, top_time;
141 static unsigned int top_name_width = 15;
142 int output_methods;
143 enum x_initialiser_state x_initialised = NO;
144 static volatile int g_signal_pending;
145 /* Update interval */
146 double update_interval;
147
148
149 /* prototypes for internally used functions */
150 static void signal_handler(int);
151 static void print_version(void) __attribute__((noreturn));
152 static void reload_config(void);
153 static void clean_up(void);
154 static void generate_text_internal(char *, int, struct text_object,
155                                    struct information *);
156 static int extract_variable_text_internal(struct text_object *,
157                                           const char *, char);
158
159 static void print_version(void)
160 {
161         printf(PACKAGE_NAME" "VERSION" compiled "BUILD_DATE" for "BUILD_ARCH"\n");
162
163         printf("\nCompiled in features:\n\n"
164                    "System config file: "SYSTEM_CONFIG_FILE"\n\n"
165 #ifdef X11
166                    " X11:\n"
167 # ifdef HAVE_XDAMAGE
168                    "  * Xdamage extension\n"
169 # endif /* HAVE_XDAMAGE */
170 # ifdef HAVE_XDBE
171                    "  * XDBE (double buffer extension)\n"
172 # endif /* HAVE_XDBE */
173 # ifdef XFT
174                    "  * Xft\n"
175 # endif /* XFT */
176 #endif /* X11 */
177                    "\n Music detection:\n"
178 #ifdef AUDACIOUS
179                    "  * Audacious\n"
180 #endif /* AUDACIOUS */
181 #ifdef BMPX
182                    "  * BMPx\n"
183 #endif /* BMPX */
184 #ifdef MPD
185                    "  * MPD\n"
186 #endif /* MPD */
187 #ifdef MOC
188                    "  * MOC\n"
189 #endif /* MOC */
190 #ifdef XMMS2
191                    "  * XMMS2\n"
192 #endif /* XMMS2 */
193                    "\n General:\n"
194 #ifdef HAVE_OPENMP
195                    "  * OpenMP\n"
196 #endif /* HAVE_OPENMP */
197 #ifdef MATH
198                    "  * math\n"
199 #endif /* Math */
200 #ifdef HDDTEMP
201                    "  * hddtemp\n"
202 #endif /* HDDTEMP */
203 #ifdef TCP_PORT_MONITOR
204                    "  * portmon\n"
205 #endif /* TCP_PORT_MONITOR */
206 #ifdef RSS
207                    "  * RSS\n"
208 #endif /* RSS */
209 #ifdef HAVE_LUA
210                    "  * Lua\n"
211 #endif /* HAVE_LUA */
212 #ifdef HAVE_IWLIB
213                    "  * wireless\n"
214 #endif /* HAVE_IWLIB */
215 #ifdef IBM
216                    "  * support for IBM/Lenovo notebooks\n"
217 #endif /* IBM */
218 #ifdef NVIDIA
219                    "  * nvidia\n"
220 #endif /* NVIDIA */
221 #ifdef EVE
222                    "  * eve-online\n"
223 #endif /* EVE */
224 #ifdef CONFIG_OUTPUT
225                    "  * config-output\n"
226 #endif /* CONFIG_OUTPUT */
227 #ifdef IMLIB2
228                    "  * IMLIB2\n"
229 #endif /* IMLIB2 */
230 #ifdef MIXER_IS_ALSA
231                    "  * ALSA mixer support\n"
232 #endif /* MIXER_IS_ALSA */
233 #ifdef APCUPSD
234                         "  * apcupsd\n"
235 #endif /* APCUPSD */
236         );
237
238         exit(0);
239 }
240
241 static const char *suffixes[] = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "" };
242
243
244 #ifdef X11
245
246 static void X11_destroy_window(void);
247 static void X11_create_window(void);
248 static void X11_initialisation(void);
249
250 struct _x11_stuff_s {
251         Region region;
252 #ifdef HAVE_XDAMAGE
253         Damage damage;
254         XserverRegion region2, part;
255         int event_base, error_base;
256 #endif
257 } x11_stuff;
258
259 /* text size */
260
261 static int text_start_x, text_start_y;  /* text start position in window */
262 static int text_width, text_height;
263
264 /* alignments */
265 enum alignment {
266         TOP_LEFT = 1,
267         TOP_RIGHT,
268         TOP_MIDDLE,
269         BOTTOM_LEFT,
270         BOTTOM_RIGHT,
271         BOTTOM_MIDDLE,
272         MIDDLE_LEFT,
273         MIDDLE_RIGHT,
274         NONE
275 };
276
277 /* display to connect to */
278 static char *disp = NULL;
279
280 #endif /* X11 */
281
282 /* struct that has all info to be shared between
283  * instances of the same text object */
284 struct information info;
285
286 /* default config file */
287 static char *current_config;
288
289 /* set to 1 if you want all text to be in uppercase */
290 static unsigned int stuff_in_upper_case;
291
292 /* Run how many times? */
293 static unsigned long total_run_times;
294
295 /* fork? */
296 static int fork_to_background;
297
298 static int cpu_avg_samples, net_avg_samples, diskio_avg_samples;
299
300 /* filenames for output */
301 char *overwrite_file = NULL; FILE *overwrite_fpointer = NULL;
302 char *append_file = NULL; FILE *append_fpointer = NULL;
303
304 #ifdef X11
305
306 static int show_graph_scale;
307 static int show_graph_range;
308
309 /* Position on the screen */
310 static int text_alignment;
311 static int gap_x, gap_y;
312
313 /* border */
314 static int draw_borders;
315 static int draw_graph_borders;
316 static int stippled_borders;
317
318 static int draw_shades, draw_outline;
319
320 static int border_margin, border_width;
321
322 static long default_fg_color, default_bg_color, default_out_color;
323
324 /* create own window or draw stuff to root? */
325 static int set_transparent = 0;
326
327 #ifdef OWN_WINDOW
328 static int own_window = 0;
329 static int background_colour = 0;
330
331 /* fixed size/pos is set if wm/user changes them */
332 static int fixed_size = 0, fixed_pos = 0;
333 #endif
334
335 static int minimum_width, minimum_height;
336 static int maximum_width;
337
338 #endif /* X11 */
339
340 #ifdef __OpenBSD__
341 static int sensor_device;
342 #endif
343
344 static long color0, color1, color2, color3, color4, color5, color6, color7,
345         color8, color9;
346
347 #define MAX_TEMPLATES 10
348 static char *template[MAX_TEMPLATES];
349
350 /* maximum size of config TEXT buffer, i.e. below TEXT line. */
351 static unsigned int max_user_text = MAX_USER_TEXT_DEFAULT;
352
353 /* maximum size of individual text buffers, ie $exec buffer size */
354 unsigned int text_buffer_size = DEFAULT_TEXT_BUFFER_SIZE;
355
356 #ifdef HAVE_ICONV
357 #define CODEPAGE_LENGTH 20
358 long iconv_selected;
359 long iconv_count = 0;
360 char iconv_converting;
361 static iconv_t **iconv_cd = 0;
362
363 int register_iconv(iconv_t *new_iconv)
364 {
365         iconv_cd = realloc(iconv_cd, sizeof(iconv_t *) * (iconv_count + 1));
366         if (!iconv_cd) {
367                 CRIT_ERR("Out of memory");
368         }
369         iconv_cd[iconv_count] = malloc(sizeof(iconv_t));
370         if (!iconv_cd[iconv_count]) {
371                 CRIT_ERR("Out of memory");
372         }
373         memcpy(iconv_cd[iconv_count], new_iconv, sizeof(iconv_t));
374         iconv_count++;
375         return iconv_count;
376 }
377
378 void free_iconv(void)
379 {
380         if (iconv_cd) {
381                 long i;
382
383                 for (i = 0; i < iconv_count; i++) {
384                         if (iconv_cd[i]) {
385                                 iconv_close(*iconv_cd[i]);
386                                 free(iconv_cd[i]);
387                         }
388                 }
389                 free(iconv_cd);
390         }
391         iconv_cd = 0;
392 }
393
394 #endif
395
396 /* UTF-8 */
397 int utf8_mode = 0;
398
399 /* no buffers in used memory? */
400 int no_buffers;
401
402 /* pad percentages to decimals? */
403 static int pad_percents = 0;
404
405 static char *global_text = 0;
406 long global_text_lines;
407
408 static int total_updates;
409 static int updatereset;
410
411 int check_contains(char *f, char *s)
412 {
413         int ret = 0;
414         FILE *where = open_file(f, 0);
415
416         if (where) {
417                 char buf1[256];
418
419                 while (fgets(buf1, 256, where)) {
420                         if (strstr(buf1, s)) {
421                                 ret = 1;
422                                 break;
423                         }
424                 }
425                 fclose(where);
426         } else {
427                 ERR("Could not open the file");
428         }
429         return ret;
430 }
431
432 #ifdef X11
433 static inline int calc_text_width(const char *s, int l)
434 {
435         if ((output_methods & TO_X) == 0)
436                 return 0;
437 #ifdef XFT
438         if (use_xft) {
439                 XGlyphInfo gi;
440
441                 if (utf8_mode) {
442                         XftTextExtentsUtf8(display, fonts[selected_font].xftfont,
443                                 (const FcChar8 *) s, l, &gi);
444                 } else {
445                         XftTextExtents8(display, fonts[selected_font].xftfont,
446                                 (const FcChar8 *) s, l, &gi);
447                 }
448                 return gi.xOff;
449         } else
450 #endif
451         {
452                 return XTextWidth(fonts[selected_font].font, s, l);
453         }
454 }
455 #endif /* X11 */
456
457 /* formatted text to render on screen, generated in generate_text(),
458  * drawn in draw_stuff() */
459
460 static char *text_buffer;
461
462 #ifdef X11
463 static unsigned int special_index;      /* used when drawing */
464 #endif /* X11 */
465
466 /* quite boring functions */
467
468 static inline void for_each_line(char *b, void f(char *))
469 {
470         char *ps, *pe;
471
472         for (ps = b, pe = b; *pe; pe++) {
473                 if (*pe == '\n') {
474                         *pe = '\0';
475                         f(ps);
476                         *pe = '\n';
477                         ps = pe + 1;
478                 }
479         }
480
481         if (ps < pe) {
482                 f(ps);
483         }
484 }
485
486 static void convert_escapes(char *buf)
487 {
488         char *p = buf, *s = buf;
489
490         while (*s) {
491                 if (*s == '\\') {
492                         s++;
493                         if (*s == 'n') {
494                                 *p++ = '\n';
495                         } else if (*s == '\\') {
496                                 *p++ = '\\';
497                         }
498                         s++;
499                 } else {
500                         *p++ = *s++;
501                 }
502         }
503         *p = '\0';
504 }
505
506 /* Prints anything normally printed with snprintf according to the current value
507  * of use_spacer.  Actually slightly more flexible than snprintf, as you can
508  * safely specify the destination buffer as one of your inputs.  */
509 int spaced_print(char *buf, int size, const char *format, int width, ...)
510 {
511         int len = 0;
512         va_list argp;
513         char *tempbuf;
514
515         if (size < 1) {
516                 return 0;
517         }
518         tempbuf = malloc(size * sizeof(char));
519
520         // Passes the varargs along to vsnprintf
521         va_start(argp, width);
522         vsnprintf(tempbuf, size, format, argp);
523         va_end(argp);
524
525         switch (use_spacer) {
526                 case NO_SPACER:
527                         len = snprintf(buf, size, "%s", tempbuf);
528                         break;
529                 case LEFT_SPACER:
530                         len = snprintf(buf, size, "%*s", width, tempbuf);
531                         break;
532                 case RIGHT_SPACER:
533                         len = snprintf(buf, size, "%-*s", width, tempbuf);
534                         break;
535         }
536         free(tempbuf);
537         return len;
538 }
539
540 /* print percentage values
541  *
542  * - i.e., unsigned values between 0 and 100
543  * - respect the value of pad_percents */
544 static int percent_print(char *buf, int size, unsigned value)
545 {
546         return spaced_print(buf, size, "%u", pad_percents, value);
547 }
548
549 /* converts from bytes to human readable format (K, M, G, T)
550  *
551  * The algorithm always divides by 1024, as unit-conversion of byte
552  * counts suggests. But for output length determination we need to
553  * compare with 1000 here, as we print in decimal form. */
554 static void human_readable(long long num, char *buf, int size)
555 {
556         const char **suffix = suffixes;
557         float fnum;
558         int precision;
559         int width;
560         const char *format;
561
562         if (short_units) {
563                 width = 5;
564                 format = "%.*f%.1s";
565         } else {
566                 width = 7;
567                 format = "%.*f%-3s";
568         }
569
570         if (llabs(num) < 1000LL) {
571                 spaced_print(buf, size, format, width, 0, (float)num, *suffix);
572                 return;
573         }
574
575         while (llabs(num / 1024) >= 1000LL && **(suffix + 2)) {
576                 num /= 1024;
577                 suffix++;
578         }
579
580         suffix++;
581         fnum = num / 1024.0;
582
583         /* fnum should now be < 1000, so looks like 'AAA.BBBBB'
584          *
585          * The goal is to always have a significance of 3, by
586          * adjusting the decimal part of the number. Sample output:
587          *  123MiB
588          * 23.4GiB
589          * 5.12B   
590          * so the point of alignment resides between number and unit. The
591          * upside of this is that there is minimal padding necessary, though
592          * there should be a way to make alignment take place at the decimal
593          * dot (then with fixed width decimal part). 
594          *
595          * Note the repdigits below: when given a precision value, printf()
596          * rounds the float to it, not just cuts off the remaining digits. So
597          * e.g. 99.95 with a precision of 1 gets 100.0, which again should be
598          * printed with a precision of 0. Yay. */
599
600         precision = 0;          /* print 100-999 without decimal part */
601         if (fnum < 99.95)
602                 precision = 1;  /* print 10-99 with one decimal place */
603         if (fnum < 9.995)
604                 precision = 2;  /* print 0-9 with two decimal places */
605
606         spaced_print(buf, size, format, width, precision, fnum, *suffix);
607 }
608
609 /* global object list root element */
610 static struct text_object global_root_object;
611
612 static inline void read_exec(const char *data, char *buf, const int size)
613 {
614         FILE *fp = popen(data, "r");
615         int length = fread(buf, 1, size, fp);
616
617         pclose(fp);
618         buf[length] = '\0';
619         if (length > 0 && buf[length - 1] == '\n') {
620                 buf[length - 1] = '\0';
621         }
622 }
623
624 void *threaded_exec(void *) __attribute__((noreturn));
625
626 void *threaded_exec(void *arg)
627 {
628         char *buff, *p2;
629         struct text_object *obj = (struct text_object *)arg;
630
631         while (1) {
632                 buff = malloc(text_buffer_size);
633                 read_exec(obj->data.texeci.cmd, buff,
634                         text_buffer_size);
635                 p2 = buff;
636                 while (*p2) {
637                         if (*p2 == '\001') {
638                                 *p2 = ' ';
639                         }
640                         p2++;
641                 }
642                 timed_thread_lock(obj->data.texeci.p_timed_thread);
643                 strncpy(obj->data.texeci.buffer, buff, text_buffer_size);
644                 timed_thread_unlock(obj->data.texeci.p_timed_thread);
645                 free(buff);
646                 if (timed_thread_test(obj->data.texeci.p_timed_thread, 0)) {
647                         timed_thread_exit(obj->data.texeci.p_timed_thread);
648                 }
649         }
650         /* never reached */
651 }
652
653 static struct text_object *new_text_object_internal(void)
654 {
655         struct text_object *obj = malloc(sizeof(struct text_object));
656         memset(obj, 0, sizeof(struct text_object));
657         return obj;
658 }
659
660 /*
661  * Frees the list of text objects root points to.  When internal = 1, it won't
662  * free global objects.
663  */
664 static void free_text_objects(struct text_object *root, int internal)
665 {
666         struct text_object *obj;
667
668         if (!root->prev) {
669                 return;
670         }
671
672 #define data obj->data
673         for (obj = root->prev; obj; obj = root->prev) {
674                 root->prev = obj->prev;
675                 switch (obj->type) {
676 #ifndef __OpenBSD__
677                         case OBJ_acpitemp:
678                                 close(data.i);
679                                 break;
680 #endif /* !__OpenBSD__ */
681 #ifdef __linux__
682                         case OBJ_i2c:
683                         case OBJ_platform:
684                         case OBJ_hwmon:
685                                 close(data.sysfs.fd);
686                                 break;
687 #endif /* __linux__ */
688                         case OBJ_time:
689                         case OBJ_utime:
690                                 free(data.s);
691                                 break;
692                         case OBJ_tztime:
693                                 free(data.tztime.tz);
694                                 free(data.tztime.fmt);
695                                 break;
696                         case OBJ_mboxscan:
697                                 free(data.mboxscan.args);
698                                 free(data.mboxscan.output);
699                                 break;
700                         case OBJ_mails:
701                         case OBJ_new_mails:
702                         case OBJ_seen_mails:
703                         case OBJ_unseen_mails:
704                         case OBJ_flagged_mails:
705                         case OBJ_unflagged_mails:
706                         case OBJ_forwarded_mails:
707                         case OBJ_unforwarded_mails:
708                         case OBJ_replied_mails:
709                         case OBJ_unreplied_mails:
710                         case OBJ_draft_mails:
711                         case OBJ_trashed_mails:
712                                 free(data.local_mail.box);
713                                 break;
714                         case OBJ_imap_unseen:
715                                 if (!obj->char_b) {
716                                         free(data.mail);
717                                 }
718                                 break;
719                         case OBJ_imap_messages:
720                                 if (!obj->char_b) {
721                                         free(data.mail);
722                                 }
723                                 break;
724                         case OBJ_pop3_unseen:
725                                 if (!obj->char_b) {
726                                         free(data.mail);
727                                 }
728                                 break;
729                         case OBJ_pop3_used:
730                                 if (!obj->char_b) {
731                                         free(data.mail);
732                                 }
733                                 break;
734                         case OBJ_if_empty:
735                         case OBJ_if_match:
736                                 free_text_objects(obj->sub, 1);
737                                 free(obj->sub);
738                                 /* fall through */
739                         case OBJ_if_existing:
740                         case OBJ_if_mounted:
741                         case OBJ_if_running:
742                                 free(data.ifblock.s);
743                                 free(data.ifblock.str);
744                                 break;
745                         case OBJ_tail:
746                                 free(data.tail.logfile);
747                                 free(data.tail.buffer);
748                                 break;
749                         case OBJ_text:
750                         case OBJ_font:
751                         case OBJ_image:
752                         case OBJ_eval:
753                         case OBJ_exec:
754                         case OBJ_execbar:
755 #ifdef X11
756                         case OBJ_execgauge:
757                         case OBJ_execgraph:
758 #endif
759                         case OBJ_execp:
760                                 free(data.s);
761                                 break;
762 #ifdef HAVE_ICONV
763                         case OBJ_iconv_start:
764                                 free_iconv();
765                                 break;
766 #endif
767 #ifdef __linux__
768                         case OBJ_disk_protect:
769                                 free(data.s);
770                                 break;
771                         case OBJ_if_gw:
772                                 free(data.ifblock.s);
773                                 free(data.ifblock.str);
774                         case OBJ_gw_iface:
775                         case OBJ_gw_ip:
776                                 if (info.gw_info.iface) {
777                                         free(info.gw_info.iface);
778                                         info.gw_info.iface = 0;
779                                 }
780                                 if (info.gw_info.ip) {
781                                         free(info.gw_info.ip);
782                                         info.gw_info.ip = 0;
783                                 }
784                                 break;
785                         case OBJ_ioscheduler:
786                                 if(data.s)
787                                         free(data.s);
788                                 break;
789 #endif
790 #if (defined(__FreeBSD__) || defined(__linux__))
791                         case OBJ_if_up:
792                                 free(data.ifblock.s);
793                                 free(data.ifblock.str);
794                                 break;
795 #endif
796 #ifdef XMMS2
797                         case OBJ_xmms2_artist:
798                                 if (info.xmms2.artist) {
799                                         free(info.xmms2.artist);
800                                         info.xmms2.artist = 0;
801                                 }
802                                 break;
803                         case OBJ_xmms2_album:
804                                 if (info.xmms2.album) {
805                                         free(info.xmms2.album);
806                                         info.xmms2.album = 0;
807                                 }
808                                 break;
809                         case OBJ_xmms2_title:
810                                 if (info.xmms2.title) {
811                                         free(info.xmms2.title);
812                                         info.xmms2.title = 0;
813                                 }
814                                 break;
815                         case OBJ_xmms2_genre:
816                                 if (info.xmms2.genre) {
817                                         free(info.xmms2.genre);
818                                         info.xmms2.genre = 0;
819                                 }
820                                 break;
821                         case OBJ_xmms2_comment:
822                                 if (info.xmms2.comment) {
823                                         free(info.xmms2.comment);
824                                         info.xmms2.comment = 0;
825                                 }
826                                 break;
827                         case OBJ_xmms2_url:
828                                 if (info.xmms2.url) {
829                                         free(info.xmms2.url);
830                                         info.xmms2.url = 0;
831                                 }
832                                 break;
833                         case OBJ_xmms2_date:
834                                 if (info.xmms2.date) {
835                                         free(info.xmms2.date);
836                                         info.xmms2.date = 0;
837                                 }
838                                 break;
839                         case OBJ_xmms2_status:
840                                 if (info.xmms2.status) {
841                                         free(info.xmms2.status);
842                                         info.xmms2.status = 0;
843                                 }
844                                 break;
845                         case OBJ_xmms2_playlist:
846                                 if (info.xmms2.playlist) {
847                                         free(info.xmms2.playlist);
848                                         info.xmms2.playlist = 0;
849                                 }
850                                 break;
851                         case OBJ_xmms2_smart:
852                                 if (info.xmms2.artist) {
853                                         free(info.xmms2.artist);
854                                         info.xmms2.artist = 0;
855                                 }
856                                 if (info.xmms2.title) {
857                                         free(info.xmms2.title);
858                                         info.xmms2.title = 0;
859                                 }
860                                 if (info.xmms2.url) {
861                                         free(info.xmms2.url);
862                                         info.xmms2.url = 0;
863                                 }
864                                 break;
865 #endif
866 #ifdef BMPX
867                         case OBJ_bmpx_title:
868                         case OBJ_bmpx_artist:
869                         case OBJ_bmpx_album:
870                         case OBJ_bmpx_track:
871                         case OBJ_bmpx_uri:
872                         case OBJ_bmpx_bitrate:
873                                 break;
874 #endif
875 #ifdef EVE
876                         case OBJ_eve:
877                                 break;
878 #endif
879 #ifdef RSS
880                         case OBJ_rss:
881                                 free(data.rss.uri);
882                                 free(data.rss.action);
883                                 break;
884 #endif
885 #ifdef HAVE_LUA
886                         case OBJ_lua:
887                         case OBJ_lua_parse:
888                         case OBJ_lua_read_parse:
889                         case OBJ_lua_bar:
890 #ifdef X11
891                         case OBJ_lua_graph:
892                         case OBJ_lua_gauge:
893 #endif /* X11 */
894                                 free(data.s);
895                                 break;
896 #endif /* HAVE_LUA */
897                         case OBJ_pre_exec:
898                                 break;
899 #ifndef __OpenBSD__
900                         case OBJ_battery:
901                                 free(data.s);
902                                 break;
903                         case OBJ_battery_short:
904                                 free(data.s);
905                                 break;
906                         case OBJ_battery_time:
907                                 free(data.s);
908                                 break;
909 #endif /* !__OpenBSD__ */
910                         case OBJ_execpi:
911                         case OBJ_execi:
912                         case OBJ_execibar:
913 #ifdef X11
914                         case OBJ_execigraph:
915                         case OBJ_execigauge:
916 #endif /* X11 */
917                                 free(data.execi.cmd);
918                                 free(data.execi.buffer);
919                                 break;
920                         case OBJ_texeci:
921                                 free(data.texeci.cmd);
922                                 free(data.texeci.buffer);
923                                 break;
924                         case OBJ_nameserver:
925                                 free_dns_data();
926                                 break;
927                         case OBJ_top:
928                         case OBJ_top_mem:
929                         case OBJ_top_time:
930                                 if (info.first_process && !internal) {
931                                         free_all_processes();
932                                         info.first_process = NULL;
933                                 }
934                                 if (data.top.s) free(data.top.s);
935                                 break;
936 #ifdef HDDTEMP
937                         case OBJ_hddtemp:
938                                 free(data.hddtemp.dev);
939                                 free(data.hddtemp.addr);
940                                 if (data.hddtemp.temp)
941                                         free(data.hddtemp.temp);
942                                 break;
943 #endif /* HDDTEMP */
944                         case OBJ_entropy_avail:
945                         case OBJ_entropy_poolsize:
946                         case OBJ_entropy_bar:
947                                 break;
948                         case OBJ_user_names:
949                                 if (info.users.names) {
950                                         free(info.users.names);
951                                         info.users.names = 0;
952                                 }
953                                 break;
954                         case OBJ_user_terms:
955                                 if (info.users.terms) {
956                                         free(info.users.terms);
957                                         info.users.terms = 0;
958                                 }
959                                 break;
960                         case OBJ_user_times:
961                                 if (info.users.times) {
962                                         free(info.users.times);
963                                         info.users.times = 0;
964                                 }
965                                 break;
966 #ifdef IBM
967                         case OBJ_smapi:
968                         case OBJ_smapi_bat_perc:
969                         case OBJ_smapi_bat_temp:
970                         case OBJ_smapi_bat_power:
971                                 free(data.s);
972                                 break;
973                         case OBJ_if_smapi_bat_installed:
974                                 free(data.ifblock.s);
975                                 free(data.ifblock.str);
976                                 break;
977 #endif /* IBM */
978 #ifdef NVIDIA
979                         case OBJ_nvidia:
980                                 break;
981 #endif /* NVIDIA */
982 #ifdef MPD
983                         case OBJ_mpd_title:
984                         case OBJ_mpd_artist:
985                         case OBJ_mpd_album:
986                         case OBJ_mpd_random:
987                         case OBJ_mpd_repeat:
988                         case OBJ_mpd_vol:
989                         case OBJ_mpd_bitrate:
990                         case OBJ_mpd_status:
991                         case OBJ_mpd_bar:
992                         case OBJ_mpd_elapsed:
993                         case OBJ_mpd_length:
994                         case OBJ_mpd_track:
995                         case OBJ_mpd_name:
996                         case OBJ_mpd_file:
997                         case OBJ_mpd_percent:
998                         case OBJ_mpd_smart:
999                         case OBJ_if_mpd_playing:
1000                                 free_mpd();
1001                                 break;
1002 #endif /* MPD */
1003 #ifdef MOC
1004                         case OBJ_moc_state:
1005                         case OBJ_moc_file:
1006                         case OBJ_moc_title:
1007                         case OBJ_moc_artist:
1008                         case OBJ_moc_song:
1009                         case OBJ_moc_album:
1010                         case OBJ_moc_totaltime:
1011                         case OBJ_moc_timeleft:
1012                         case OBJ_moc_curtime:
1013                         case OBJ_moc_bitrate:
1014                         case OBJ_moc_rate:
1015                                 free_moc();
1016                                 break;
1017 #endif /* MOC */
1018                         case OBJ_scroll:
1019                                 free(data.scroll.text);
1020                                 free_text_objects(obj->sub, 1);
1021                                 free(obj->sub);
1022                                 break;
1023                         case OBJ_combine:
1024                                 free(data.combine.left);
1025                                 free(data.combine.seperation);
1026                                 free(data.combine.right);
1027                                 free_text_objects(obj->sub, 1);
1028                                 free(obj->sub);
1029                                 break;
1030 #ifdef APCUPSD
1031                         case OBJ_apcupsd:
1032                         case OBJ_apcupsd_name:
1033                         case OBJ_apcupsd_model:
1034                         case OBJ_apcupsd_upsmode:
1035                         case OBJ_apcupsd_cable:
1036                         case OBJ_apcupsd_status:
1037                         case OBJ_apcupsd_linev:
1038                         case OBJ_apcupsd_load:
1039                         case OBJ_apcupsd_loadbar:
1040 #ifdef X11
1041                         case OBJ_apcupsd_loadgraph:
1042                         case OBJ_apcupsd_loadgauge:
1043 #endif /* X11 */
1044                         case OBJ_apcupsd_charge:
1045                         case OBJ_apcupsd_timeleft:
1046                         case OBJ_apcupsd_temp:
1047                         case OBJ_apcupsd_lastxfer:
1048                                 break;
1049 #endif /* APCUPSD */
1050                 }
1051                 free(obj);
1052         }
1053 #undef data
1054 }
1055
1056 #ifdef X11
1057 void scan_mixer_bar(const char *arg, int *a, int *w, int *h)
1058 {
1059         char buf1[64];
1060         int n;
1061
1062         if (arg && sscanf(arg, "%63s %n", buf1, &n) >= 1) {
1063                 *a = mixer_init(buf1);
1064                 scan_bar(arg + n, w, h);
1065         } else {
1066                 *a = mixer_init(NULL);
1067                 scan_bar(arg, w, h);
1068         }
1069 }
1070 #endif /* X11 */
1071
1072 /* strip a leading /dev/ if any, following symlinks first
1073  *
1074  * BEWARE: this function returns a pointer to static content
1075  *         which gets overwritten in consecutive calls. I.e.:
1076  *         this function is NOT reentrant.
1077  */
1078 static const char *dev_name(const char *path)
1079 {
1080         static char buf[255];   /* should be enough for pathnames */
1081         ssize_t buflen;
1082
1083         if (!path)
1084                 return NULL;
1085
1086 #define DEV_NAME(x) \
1087   x != NULL && strlen(x) > 5 && strncmp(x, "/dev/", 5) == 0 ? x + 5 : x
1088         if ((buflen = readlink(path, buf, 254)) == -1)
1089                 return DEV_NAME(path);
1090         buf[buflen] = '\0';
1091         return DEV_NAME(buf);
1092 #undef DEV_NAME
1093 }
1094
1095 static int parse_top_args(const char *s, const char *arg, struct text_object *obj)
1096 {
1097         char buf[64];
1098         int n;
1099
1100         if (obj->data.top.was_parsed) {
1101                 return 1;
1102         }
1103         obj->data.top.was_parsed = 1;
1104
1105         if (arg && !obj->data.top.s) {
1106                 obj->data.top.s = strndup(arg, text_buffer_size);
1107         }
1108
1109         need_mask |= (1 << INFO_TOP);
1110
1111         if (s[3] == 0) {
1112                 obj->type = OBJ_top;
1113                 top_cpu = 1;
1114         } else if (strcmp(&s[3], "_mem") == EQUAL) {
1115                 obj->type = OBJ_top_mem;
1116                 top_mem = 1;
1117         } else if (strcmp(&s[3], "_time") == EQUAL) {
1118                 obj->type = OBJ_top_time;
1119                 top_time = 1;
1120         } else {
1121                 ERR("Must be top, top_mem or top_time");
1122                 return 0;
1123         }
1124
1125         if (!arg) {
1126                 ERR("top needs arguments");
1127                 return 0;
1128         }
1129
1130         if (sscanf(arg, "%63s %i", buf, &n) == 2) {
1131                 if (strcmp(buf, "name") == EQUAL) {
1132                         obj->data.top.type = TOP_NAME;
1133                 } else if (strcmp(buf, "cpu") == EQUAL) {
1134                         obj->data.top.type = TOP_CPU;
1135                 } else if (strcmp(buf, "pid") == EQUAL) {
1136                         obj->data.top.type = TOP_PID;
1137                 } else if (strcmp(buf, "mem") == EQUAL) {
1138                         obj->data.top.type = TOP_MEM;
1139                 } else if (strcmp(buf, "time") == EQUAL) {
1140                         obj->data.top.type = TOP_TIME;
1141                 } else if (strcmp(buf, "mem_res") == EQUAL) {
1142                         obj->data.top.type = TOP_MEM_RES;
1143                 } else if (strcmp(buf, "mem_vsize") == EQUAL) {
1144                         obj->data.top.type = TOP_MEM_VSIZE;
1145                 } else {
1146                         ERR("invalid type arg for top");
1147                         ERR("must be one of: name, cpu, pid, mem, time, mem_res, mem_vsize");
1148                         return 0;
1149                 }
1150                 if (n < 1 || n > 10) {
1151                         ERR("invalid num arg for top. Must be between 1 and 10.");
1152                         return 0;
1153                 } else {
1154                         obj->data.top.num = n - 1;
1155                 }
1156         } else {
1157                 ERR("invalid argument count for top");
1158                 return 0;
1159         }
1160         return 1;
1161 }
1162
1163 /* construct_text_object() creates a new text_object */
1164 static struct text_object *construct_text_object(const char *s,
1165                 const char *arg, long line, char allow_threaded, void **ifblock_opaque)
1166 {
1167         // struct text_object *obj = new_text_object();
1168         struct text_object *obj = new_text_object_internal();
1169
1170         obj->line = line;
1171
1172 #define OBJ(a, n) if (strcmp(s, #a) == 0) { \
1173         obj->type = OBJ_##a; need_mask |= (1ULL << n); {
1174 #define OBJ_IF(a, n) if (strcmp(s, #a) == 0) { \
1175         obj->type = OBJ_##a; need_mask |= (1ULL << n); \
1176         obj_be_ifblock_if(ifblock_opaque, obj); {
1177 #define OBJ_THREAD(a, n) if (strcmp(s, #a) == 0 && allow_threaded) { \
1178         obj->type = OBJ_##a; need_mask |= (1ULL << n); {
1179 #define END } } else
1180
1181 #define SIZE_DEFAULTS(arg) { \
1182         obj->a = default_##arg##_width; \
1183         obj->b = default_##arg##_height; \
1184 }
1185
1186 #ifdef X11
1187         if (s[0] == '#') {
1188                 obj->type = OBJ_color;
1189                 obj->data.l = get_x11_color(s);
1190         } else
1191 #endif /* X11 */
1192 #ifdef __OpenBSD__
1193         OBJ(freq, INFO_FREQ)
1194 #else
1195         OBJ(acpitemp, 0)
1196                 obj->data.i = open_acpi_temperature(arg);
1197         END OBJ(acpiacadapter, 0)
1198         END OBJ(freq, INFO_FREQ)
1199 #endif /* !__OpenBSD__ */
1200                 get_cpu_count();
1201                 if (!arg || !isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) == 0
1202                                 || (unsigned int) atoi(&arg[0]) > info.cpu_count) {
1203                         obj->data.cpu_index = 1;
1204                         /* ERR("freq: Invalid CPU number or you don't have that many CPUs! "
1205                                 "Displaying the clock for CPU 1."); */
1206                 } else {
1207                         obj->data.cpu_index = atoi(&arg[0]);
1208                 }
1209                 obj->a = 1;
1210         END OBJ(freq_g, INFO_FREQ)
1211                 get_cpu_count();
1212                 if (!arg || !isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) == 0
1213                                 || (unsigned int) atoi(&arg[0]) > info.cpu_count) {
1214                         obj->data.cpu_index = 1;
1215                         /* ERR("freq_g: Invalid CPU number or you don't have that many "
1216                                 "CPUs! Displaying the clock for CPU 1."); */
1217                 } else {
1218                         obj->data.cpu_index = atoi(&arg[0]);
1219                 }
1220                 obj->a = 1;
1221 #if defined(__linux__)
1222         END OBJ(voltage_mv, 0)
1223                 get_cpu_count();
1224                 if (!arg || !isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) == 0
1225                                 || (unsigned int) atoi(&arg[0]) > info.cpu_count) {
1226                         obj->data.cpu_index = 1;
1227                         /* ERR("voltage_mv: Invalid CPU number or you don't have that many "
1228                                 "CPUs! Displaying voltage for CPU 1."); */
1229                 } else {
1230                         obj->data.cpu_index = atoi(&arg[0]);
1231                 }
1232                 obj->a = 1;
1233         END OBJ(voltage_v, 0)
1234                 get_cpu_count();
1235                 if (!arg || !isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) == 0
1236                                 || (unsigned int) atoi(&arg[0]) > info.cpu_count) {
1237                         obj->data.cpu_index = 1;
1238                         /* ERR("voltage_v: Invalid CPU number or you don't have that many "
1239                                 "CPUs! Displaying voltage for CPU 1."); */
1240                 } else {
1241                         obj->data.cpu_index = atoi(&arg[0]);
1242                 }
1243                 obj->a = 1;
1244
1245 #ifdef HAVE_IWLIB
1246         END OBJ(wireless_essid, INFO_NET)
1247                 if (arg) {
1248                         obj->data.net = get_net_stat(arg);
1249                 } else {
1250                         CRIT_ERR("wireless_essid: needs an argument");
1251                 }
1252         END OBJ(wireless_mode, INFO_NET)
1253                 if (arg) {
1254                         obj->data.net = get_net_stat(arg);
1255                 } else {
1256                         CRIT_ERR("wireless_mode: needs an argument");
1257                 }
1258         END OBJ(wireless_bitrate, INFO_NET)
1259                 if (arg) {
1260                         obj->data.net = get_net_stat(arg);
1261                 } else {
1262                         CRIT_ERR("wireless_bitrate: needs an argument");
1263                 }
1264         END OBJ(wireless_ap, INFO_NET)
1265                 if (arg) {
1266                         obj->data.net = get_net_stat(arg);
1267                 } else {
1268                         CRIT_ERR("wireless_ap: needs an argument");
1269                 }
1270         END OBJ(wireless_link_qual, INFO_NET)
1271                 if (arg) {
1272                         obj->data.net = get_net_stat(arg);
1273                 } else {
1274                         CRIT_ERR("wireless_link_qual: needs an argument");
1275                 }
1276         END OBJ(wireless_link_qual_max, INFO_NET)
1277                 if (arg) {
1278                         obj->data.net = get_net_stat(arg);
1279                 } else {
1280                         CRIT_ERR("wireless_link_qual_max: needs an argument");
1281                 }
1282         END OBJ(wireless_link_qual_perc, INFO_NET)
1283                 if (arg) {
1284                         obj->data.net = get_net_stat(arg);
1285                 } else {
1286                         CRIT_ERR("wireless_link_qual_perc: needs an argument");
1287                 }
1288         END OBJ(wireless_link_bar, INFO_NET)
1289                 SIZE_DEFAULTS(bar);
1290                 if (arg) {
1291                         arg = scan_bar(arg, &obj->a, &obj->b);
1292                         obj->data.net = get_net_stat(arg);
1293                 } else {
1294                         CRIT_ERR("wireless_link_bar: needs an argument");
1295                 }
1296 #endif /* HAVE_IWLIB */
1297
1298 #endif /* __linux__ */
1299
1300 #ifndef __OpenBSD__
1301         END OBJ(acpifan, 0)
1302         END OBJ(battery, 0)
1303                 char bat[64];
1304
1305                 if (arg) {
1306                         sscanf(arg, "%63s", bat);
1307                 } else {
1308                         strcpy(bat, "BAT0");
1309                 }
1310                 obj->data.s = strndup(bat, text_buffer_size);
1311         END OBJ(battery_short, 0)
1312                 char bat[64];
1313
1314                 if (arg) {
1315                         sscanf(arg, "%63s", bat);
1316                 } else {
1317                         strcpy(bat, "BAT0");
1318                 }
1319                 obj->data.s = strndup(bat, text_buffer_size);
1320         END OBJ(battery_time, 0)
1321                 char bat[64];
1322
1323                 if (arg) {
1324                         sscanf(arg, "%63s", bat);
1325                 } else {
1326                         strcpy(bat, "BAT0");
1327                 }
1328                 obj->data.s = strndup(bat, text_buffer_size);
1329         END OBJ(battery_percent, 0)
1330                 char bat[64];
1331
1332                 if (arg) {
1333                         sscanf(arg, "%63s", bat);
1334                 } else {
1335                         strcpy(bat, "BAT0");
1336                 }
1337                 obj->data.s = strndup(bat, text_buffer_size);
1338         END OBJ(battery_bar, 0)
1339                 char bat[64];
1340                 SIZE_DEFAULTS(bar);
1341                 obj->b = 6;
1342                 if (arg) {
1343                         arg = scan_bar(arg, &obj->a, &obj->b);
1344                         sscanf(arg, "%63s", bat);
1345                 } else {
1346                         strcpy(bat, "BAT0");
1347                 }
1348                 obj->data.s = strndup(bat, text_buffer_size);
1349 #endif /* !__OpenBSD__ */
1350
1351 #if defined(__linux__)
1352         END OBJ(disk_protect, 0)
1353                 if (arg)
1354                         obj->data.s = strndup(dev_name(arg), text_buffer_size);
1355                 else
1356                         CRIT_ERR("disk_protect needs an argument");
1357         END OBJ(i8k_version, INFO_I8K)
1358         END OBJ(i8k_bios, INFO_I8K)
1359         END OBJ(i8k_serial, INFO_I8K)
1360         END OBJ(i8k_cpu_temp, INFO_I8K)
1361         END OBJ(i8k_left_fan_status, INFO_I8K)
1362         END OBJ(i8k_right_fan_status, INFO_I8K)
1363         END OBJ(i8k_left_fan_rpm, INFO_I8K)
1364         END OBJ(i8k_right_fan_rpm, INFO_I8K)
1365         END OBJ(i8k_ac_status, INFO_I8K)
1366         END OBJ(i8k_buttons_status, INFO_I8K)
1367 #if defined(IBM)
1368         END OBJ(ibm_fan, 0)
1369         END OBJ(ibm_temps, 0)
1370                 if (!arg) {
1371                         CRIT_ERR("ibm_temps: needs an argument");
1372                 }
1373                 if (!isdigit(arg[0]) || strlen(arg) >= 2 || atoi(&arg[0]) >= 8) {
1374                         obj->data.sensor = 0;
1375                         ERR("Invalid temperature sensor! Sensor number must be 0 to 7. "
1376                                 "Using 0 (CPU temp sensor).");
1377                 }
1378                 obj->data.sensor = atoi(&arg[0]);
1379         END OBJ(ibm_volume, 0)
1380         END OBJ(ibm_brightness, 0)
1381 #endif
1382         /* information from sony_laptop kernel module
1383          * /sys/devices/platform/sony-laptop */
1384         END OBJ(sony_fanspeed, 0)
1385         END OBJ_IF(if_gw, INFO_GW)
1386         END OBJ(ioscheduler, 0)
1387                 if (!arg) {
1388                         CRIT_ERR("get_ioscheduler needs an argument (e.g. hda)");
1389                         obj->data.s = 0;
1390                 } else
1391                         obj->data.s = strndup(dev_name(arg), text_buffer_size);
1392         END OBJ(laptop_mode, 0)
1393         END OBJ(pb_battery, 0)
1394                 if (arg && strcmp(arg, "status") == EQUAL) {
1395                         obj->data.i = PB_BATT_STATUS;
1396                 } else if (arg && strcmp(arg, "percent") == EQUAL) {
1397                         obj->data.i = PB_BATT_PERCENT;
1398                 } else if (arg && strcmp(arg, "time") == EQUAL) {
1399                         obj->data.i = PB_BATT_TIME;
1400                 } else {
1401                         ERR("pb_battery: needs one argument: status, percent or time");
1402                         free(obj);
1403                         return NULL;
1404                 }
1405
1406 #endif /* __linux__ */
1407 #if (defined(__FreeBSD__) || defined(__linux__))
1408         END OBJ_IF(if_up, 0)
1409                 if (!arg) {
1410                         ERR("if_up needs an argument");
1411                         obj->data.ifblock.s = 0;
1412                 } else {
1413                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
1414                 }
1415 #endif
1416 #if defined(__OpenBSD__)
1417         END OBJ(obsd_sensors_temp, 0)
1418                 if (!arg) {
1419                         CRIT_ERR("obsd_sensors_temp: needs an argument");
1420                 }
1421                 if (!isdigit(arg[0]) || atoi(&arg[0]) < 0
1422                                 || atoi(&arg[0]) > OBSD_MAX_SENSORS - 1) {
1423                         obj->data.sensor = 0;
1424                         ERR("Invalid temperature sensor number!");
1425                 }
1426                 obj->data.sensor = atoi(&arg[0]);
1427         END OBJ(obsd_sensors_fan, 0)
1428                 if (!arg) {
1429                         CRIT_ERR("obsd_sensors_fan: needs 2 arguments (device and sensor "
1430                                 "number)");
1431                 }
1432                 if (!isdigit(arg[0]) || atoi(&arg[0]) < 0
1433                                 || atoi(&arg[0]) > OBSD_MAX_SENSORS - 1) {
1434                         obj->data.sensor = 0;
1435                         ERR("Invalid fan sensor number!");
1436                 }
1437                 obj->data.sensor = atoi(&arg[0]);
1438         END OBJ(obsd_sensors_volt, 0)
1439                 if (!arg) {
1440                         CRIT_ERR("obsd_sensors_volt: needs 2 arguments (device and sensor "
1441                                 "number)");
1442                 }
1443                 if (!isdigit(arg[0]) || atoi(&arg[0]) < 0
1444                                 || atoi(&arg[0]) > OBSD_MAX_SENSORS - 1) {
1445                         obj->data.sensor = 0;
1446                         ERR("Invalid voltage sensor number!");
1447                 }
1448                 obj->data.sensor = atoi(&arg[0]);
1449         END OBJ(obsd_vendor, 0)
1450         END OBJ(obsd_product, 0)
1451 #endif /* __OpenBSD__ */
1452         END OBJ(buffers, INFO_BUFFERS)
1453         END OBJ(cached, INFO_BUFFERS)
1454 #define SCAN_CPU(__arg, __var) { \
1455         int __offset = 0; \
1456         if (__arg && sscanf(__arg, " cpu%u %n", &__var, &__offset) > 0) \
1457                 __arg += __offset; \
1458         else \
1459                 __var = 0; \
1460 }
1461         END OBJ(cpu, INFO_CPU)
1462                 SCAN_CPU(arg, obj->data.cpu_index);
1463                 DBGP2("Adding $cpu for CPU %d", obj->data.cpu_index);
1464 #ifdef X11
1465         END OBJ(cpugauge, INFO_CPU)
1466                 SIZE_DEFAULTS(gauge);
1467                 SCAN_CPU(arg, obj->data.cpu_index);
1468                 scan_gauge(arg, &obj->a, &obj->b);
1469                 DBGP2("Adding $cpugauge for CPU %d", obj->data.cpu_index);
1470 #endif /* X11 */
1471         END OBJ(cpubar, INFO_CPU)
1472                 SIZE_DEFAULTS(bar);
1473                 SCAN_CPU(arg, obj->data.cpu_index);
1474                 scan_bar(arg, &obj->a, &obj->b);
1475                 DBGP2("Adding $cpubar for CPU %d", obj->data.cpu_index);
1476 #ifdef X11
1477         END OBJ(cpugraph, INFO_CPU)
1478                 SIZE_DEFAULTS(graph);
1479                 SCAN_CPU(arg, obj->data.cpu_index);
1480                 scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
1481                         &obj->e, &obj->char_a, &obj->char_b);
1482                 DBGP2("Adding $cpugraph for CPU %d", obj->data.cpu_index);
1483         END OBJ(loadgraph, INFO_LOADAVG)
1484                 char *buf = 0;
1485                 SIZE_DEFAULTS(graph);
1486                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
1487                                 &obj->e, &obj->char_a, &obj->char_b);
1488                 if (buf) {
1489                         int a = 1, r = 3;
1490                         if (arg) {
1491                                 r = sscanf(arg, "%d", &a);
1492                         }
1493                         obj->data.loadavg[0] = (r >= 1) ? (unsigned char) a : 0;
1494                         free(buf);
1495                 }
1496 #endif /* X11 */
1497         END OBJ(diskio, INFO_DISKIO)
1498                 obj->data.diskio = prepare_diskio_stat(dev_name(arg));
1499         END OBJ(diskio_read, INFO_DISKIO)
1500                 obj->data.diskio = prepare_diskio_stat(dev_name(arg));
1501         END OBJ(diskio_write, INFO_DISKIO)
1502                 obj->data.diskio = prepare_diskio_stat(dev_name(arg));
1503 #ifdef X11
1504         END OBJ(diskiograph, INFO_DISKIO)
1505                 char *buf = 0;
1506                 SIZE_DEFAULTS(graph);
1507                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
1508                                 &obj->e, &obj->char_a, &obj->char_b);
1509
1510                 obj->data.diskio = prepare_diskio_stat(dev_name(buf));
1511                 if (buf)
1512                         free(buf);
1513         END OBJ(diskiograph_read, INFO_DISKIO)
1514                 char *buf = 0;
1515                 SIZE_DEFAULTS(graph);
1516                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
1517                                 &obj->e, &obj->char_a, &obj->char_b);
1518
1519                 obj->data.diskio = prepare_diskio_stat(dev_name(buf));
1520                 if (buf)
1521                         free(buf);
1522         END OBJ(diskiograph_write, INFO_DISKIO)
1523                 char *buf = 0;
1524                 SIZE_DEFAULTS(graph);
1525                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
1526                                 &obj->e, &obj->char_a, &obj->char_b);
1527
1528                 obj->data.diskio = prepare_diskio_stat(dev_name(buf));
1529                 if (buf)
1530                         free(buf);
1531 #endif /* X11 */
1532         END OBJ(color, 0)
1533 #ifdef X11
1534                 if (output_methods & TO_X) {
1535                         obj->data.l = arg ? get_x11_color(arg) : default_fg_color;
1536                 }
1537 #endif /* X11 */
1538         END OBJ(color0, 0)
1539                 obj->data.l = color0;
1540         END OBJ(color1, 0)
1541                 obj->data.l = color1;
1542         END OBJ(color2, 0)
1543                 obj->data.l = color2;
1544         END OBJ(color3, 0)
1545                 obj->data.l = color3;
1546         END OBJ(color4, 0)
1547                 obj->data.l = color4;
1548         END OBJ(color5, 0)
1549                 obj->data.l = color5;
1550         END OBJ(color6, 0)
1551                 obj->data.l = color6;
1552         END OBJ(color7, 0)
1553                 obj->data.l = color7;
1554         END OBJ(color8, 0)
1555                 obj->data.l = color8;
1556         END OBJ(color9, 0)
1557                 obj->data.l = color9;
1558 #ifdef X11
1559         END OBJ(font, 0)
1560                 obj->data.s = scan_font(arg);
1561 #endif /* X11 */
1562         END OBJ(conky_version, 0)
1563         END OBJ(conky_build_date, 0)
1564         END OBJ(conky_build_arch, 0)
1565         END OBJ(downspeed, INFO_NET)
1566                 if (arg) {
1567                         obj->data.net = get_net_stat(arg);
1568                 } else {
1569                         CRIT_ERR("downspeed needs argument");
1570                 }
1571         END OBJ(downspeedf, INFO_NET)
1572                 if (arg) {
1573                         obj->data.net = get_net_stat(arg);
1574                 } else {
1575                         CRIT_ERR("downspeedf needs argument");
1576                 }
1577 #ifdef X11
1578         END OBJ(downspeedgraph, INFO_NET)
1579                 char *buf = 0;
1580                 SIZE_DEFAULTS(graph);
1581                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
1582                                 &obj->e, &obj->char_a, &obj->char_b);
1583
1584                 // default to DEFAULTNETDEV
1585                 buf = strndup(buf ? buf : "DEFAULTNETDEV", text_buffer_size);
1586                 obj->data.net = get_net_stat(buf);
1587                 free(buf);
1588 #endif /* X11 */
1589         END OBJ(else, 0)
1590                 obj_be_ifblock_else(ifblock_opaque, obj);
1591         END OBJ(endif, 0)
1592                 obj_be_ifblock_endif(ifblock_opaque, obj);
1593         END OBJ(eval, 0)
1594                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
1595         END OBJ(image, 0)
1596                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
1597 #ifdef HAVE_POPEN
1598         END OBJ(exec, 0)
1599                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
1600         END OBJ(execp, 0)
1601                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
1602         END OBJ(execbar, 0)
1603                 SIZE_DEFAULTS(bar);
1604                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
1605 #ifdef X11
1606         END OBJ(execgauge, 0)
1607                 SIZE_DEFAULTS(gauge);
1608                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
1609         END OBJ(execgraph, 0)
1610                 SIZE_DEFAULTS(graph);
1611                 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
1612 #endif /* X11 */
1613         END OBJ(execibar, 0)
1614                 int n;
1615                 SIZE_DEFAULTS(bar);
1616
1617                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
1618                         char buf[256];
1619
1620                         ERR("${execibar <interval> command}");
1621                         obj->type = OBJ_text;
1622                         snprintf(buf, 256, "${%s}", s);
1623                         obj->data.s = strndup(buf, text_buffer_size);
1624                 } else {
1625                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
1626                 }
1627 #ifdef X11
1628         END OBJ(execigraph, 0)
1629                 int n;
1630                 SIZE_DEFAULTS(graph);
1631
1632                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
1633                         char buf[256];
1634
1635                         ERR("${execigraph <interval> command}");
1636                         obj->type = OBJ_text;
1637                         snprintf(buf, 256, "${%s}", s);
1638                         obj->data.s = strndup(buf, text_buffer_size);
1639                 } else {
1640                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
1641                 }
1642         END OBJ(execigauge, 0)
1643                 int n;
1644                 SIZE_DEFAULTS(gauge);
1645
1646                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
1647                         char buf[256];
1648
1649                         ERR("${execigauge <interval> command}");
1650                         obj->type = OBJ_text;
1651                         snprintf(buf, 256, "${%s}", s);
1652                         obj->data.s = strndup(buf, text_buffer_size);
1653                 } else {
1654                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
1655                 }
1656 #endif /* X11 */
1657         END OBJ(execi, 0)
1658                 int n;
1659
1660                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
1661                         char buf[256];
1662
1663                         ERR("${execi <interval> command}");
1664                         obj->type = OBJ_text;
1665                         snprintf(buf, 256, "${%s}", s);
1666                         obj->data.s = strndup(buf, text_buffer_size);
1667                 } else {
1668                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
1669                         obj->data.execi.buffer = malloc(text_buffer_size);
1670                 }
1671         END OBJ(execpi, 0)
1672                 int n;
1673
1674                 if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
1675                         char buf[256];
1676
1677                         ERR("${execi <interval> command}");
1678                         obj->type = OBJ_text;
1679                         snprintf(buf, 256, "${%s}", s);
1680                         obj->data.s = strndup(buf, text_buffer_size);
1681                 } else {
1682                         obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
1683                         obj->data.execi.buffer = malloc(text_buffer_size);
1684                 }
1685         END OBJ_THREAD(texeci, 0)
1686                         int n;
1687
1688                         if (!arg || sscanf(arg, "%f %n", &obj->data.texeci.interval, &n) <= 0) {
1689                                 char buf[256];
1690
1691                                 ERR("${texeci <interval> command}");
1692                                 obj->type = OBJ_text;
1693                                 snprintf(buf, 256, "${%s}", s);
1694                                 obj->data.s = strndup(buf, text_buffer_size);
1695                         } else {
1696                                 obj->data.texeci.cmd = strndup(arg + n, text_buffer_size);
1697                                 obj->data.texeci.buffer = malloc(text_buffer_size);
1698                         }
1699                         obj->data.texeci.p_timed_thread = NULL;
1700         END     OBJ(pre_exec, 0)
1701                 obj->type = OBJ_text;
1702         if (arg) {
1703                 char buf[2048];
1704
1705                 read_exec(arg, buf, sizeof(buf));
1706                 obj->data.s = strndup(buf, text_buffer_size);
1707         } else {
1708                 obj->data.s = strndup("", text_buffer_size);
1709         }
1710 #endif
1711         END OBJ(fs_bar, INFO_FS)
1712                 SIZE_DEFAULTS(bar);
1713                 arg = scan_bar(arg, &obj->data.fsbar.w, &obj->data.fsbar.h);
1714                 if (arg) {
1715                         while (isspace(*arg)) {
1716                                 arg++;
1717                         }
1718                         if (*arg == '\0') {
1719                                 arg = "/";
1720                         }
1721                 } else {
1722                         arg = "/";
1723                 }
1724                 obj->data.fsbar.fs = prepare_fs_stat(arg);
1725         END OBJ(fs_bar_free, INFO_FS)
1726                 SIZE_DEFAULTS(bar);
1727                 arg = scan_bar(arg, &obj->data.fsbar.w, &obj->data.fsbar.h);
1728                 if (arg) {
1729                         while (isspace(*arg)) {
1730                                 arg++;
1731                         }
1732                         if (*arg == '\0') {
1733                                 arg = "/";
1734                         }
1735                 } else {
1736                         arg = "/";
1737                 }
1738
1739                 obj->data.fsbar.fs = prepare_fs_stat(arg);
1740         END OBJ(fs_free, INFO_FS)
1741                 if (!arg) {
1742                         arg = "/";
1743                 }
1744                 obj->data.fs = prepare_fs_stat(arg);
1745         END OBJ(fs_used_perc, INFO_FS)
1746                 if (!arg) {
1747                         arg = "/";
1748                 }
1749                 obj->data.fs = prepare_fs_stat(arg);
1750         END OBJ(fs_free_perc, INFO_FS)
1751                 if (!arg) {
1752                         arg = "/";
1753                 }
1754                 obj->data.fs = prepare_fs_stat(arg);
1755         END OBJ(fs_size, INFO_FS)
1756                 if (!arg) {
1757                         arg = "/";
1758                 }
1759                 obj->data.fs = prepare_fs_stat(arg);
1760         END OBJ(fs_type, INFO_FS)
1761                 if (!arg) {
1762                         arg = "/";
1763                 }
1764                 obj->data.fs = prepare_fs_stat(arg);
1765         END OBJ(fs_used, INFO_FS)
1766                 if (!arg) {
1767                         arg = "/";
1768                 }
1769                 obj->data.fs = prepare_fs_stat(arg);
1770         END OBJ(hr, 0)
1771                 obj->data.i = arg ? atoi(arg) : 1;
1772         END OBJ(nameserver, INFO_DNS)
1773                 obj->data.i = arg ? atoi(arg) : 0;
1774         END OBJ(offset, 0)
1775                 obj->data.i = arg ? atoi(arg) : 1;
1776         END OBJ(voffset, 0)
1777                 obj->data.i = arg ? atoi(arg) : 1;
1778         END OBJ(goto, 0)
1779
1780                 if (!arg) {
1781                         ERR("goto needs arguments");
1782                         obj->type = OBJ_text;
1783                         obj->data.s = strndup("${goto}", text_buffer_size);
1784                         return NULL;
1785                 }
1786
1787                 obj->data.i = atoi(arg);
1788
1789         END OBJ(tab, 0)
1790                 int a = 10, b = 0;
1791
1792                 if (arg) {
1793                         if (sscanf(arg, "%d %d", &a, &b) != 2) {
1794                                 sscanf(arg, "%d", &b);
1795                         }
1796                 }
1797                 if (a <= 0) {
1798                         a = 1;
1799                 }
1800                 obj->data.pair.a = a;
1801                 obj->data.pair.b = b;
1802
1803 #ifdef __linux__
1804         END OBJ(i2c, INFO_SYSFS)
1805                 char buf1[64], buf2[64];
1806                 float factor, offset;
1807                 int n, found = 0;
1808
1809                 if (!arg) {
1810                         ERR("i2c needs arguments");
1811                         obj->type = OBJ_text;
1812                         // obj->data.s = strndup("${i2c}", text_buffer_size);
1813                         return NULL;
1814                 }
1815
1816 #define HWMON_RESET() {\
1817                 buf1[0] = 0; \
1818                 factor = 1.0; \
1819                 offset = 0.0; }
1820
1821                 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1822                 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1823                 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1824                 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1825
1826                 if (!found) {
1827                         ERR("i2c failed to parse arguments");
1828                         obj->type = OBJ_text;
1829                         return NULL;
1830                 }
1831                 DBGP("parsed i2c args: '%s' '%s' %d %f %f\n", buf1, buf2, n, factor, offset);
1832                 obj->data.sysfs.fd = open_i2c_sensor((*buf1) ? buf1 : 0, buf2, n,
1833                                 &obj->data.sysfs.arg, obj->data.sysfs.devtype);
1834                 strncpy(obj->data.sysfs.type, buf2, 63);
1835                 obj->data.sysfs.factor = factor;
1836                 obj->data.sysfs.offset = offset;
1837
1838         END OBJ(platform, INFO_SYSFS)
1839                 char buf1[64], buf2[64];
1840                 float factor, offset;
1841                 int n, found = 0;
1842
1843                 if (!arg) {
1844                         ERR("platform needs arguments");
1845                         obj->type = OBJ_text;
1846                         return NULL;
1847                 }
1848
1849                 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1850                 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1851                 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1852                 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1853
1854                 if (!found) {
1855                         ERR("platform failed to parse arguments");
1856                         obj->type = OBJ_text;
1857                         return NULL;
1858                 }
1859                 DBGP("parsed platform args: '%s' '%s' %d %f %f\n", buf1, buf2, n, factor, offset);
1860                 obj->data.sysfs.fd = open_platform_sensor((*buf1) ? buf1 : 0, buf2, n,
1861                                 &obj->data.sysfs.arg, obj->data.sysfs.devtype);
1862                 strncpy(obj->data.sysfs.type, buf2, 63);
1863                 obj->data.sysfs.factor = factor;
1864                 obj->data.sysfs.offset = offset;
1865
1866         END OBJ(hwmon, INFO_SYSFS)
1867                 char buf1[64], buf2[64];
1868                 float factor, offset;
1869                 int n, found = 0;
1870
1871                 if (!arg) {
1872                         ERR("hwmon needs argumanets");
1873                         obj->type = OBJ_text;
1874                         return NULL;
1875                 }
1876
1877                 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1878                 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1879                 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1880                 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1881
1882 #undef HWMON_RESET
1883
1884                 if (!found) {
1885                         ERR("hwmon failed to parse arguments");
1886                         obj->type = OBJ_text;
1887                         return NULL;
1888                 }
1889                 DBGP("parsed hwmon args: '%s' '%s' %d %f %f\n", buf1, buf2, n, factor, offset);
1890                 obj->data.sysfs.fd = open_hwmon_sensor((*buf1) ? buf1 : 0, buf2, n,
1891                                 &obj->data.sysfs.arg, obj->data.sysfs.devtype);
1892                 strncpy(obj->data.sysfs.type, buf2, 63);
1893                 obj->data.sysfs.factor = factor;
1894                 obj->data.sysfs.offset = offset;
1895
1896 #endif /* !__OpenBSD__ */
1897
1898         END
1899         /* we have three different types of top (top, top_mem and top_time). To
1900          * avoid having almost-same code three times, we have this special
1901          * handler. */
1902         if (strncmp(s, "top", 3) == EQUAL) {
1903                 if (!parse_top_args(s, arg, obj)) {
1904                         return NULL;
1905                 }
1906         } else OBJ(addr, INFO_NET)
1907                 if (arg) {
1908                         obj->data.net = get_net_stat(arg);
1909                 } else {
1910                         CRIT_ERR("addr needs argument");
1911                 }
1912 #if defined(__linux__)
1913         END OBJ(addrs, INFO_NET)
1914                 if (arg) {
1915                         obj->data.net = get_net_stat(arg);
1916                 } else {
1917                         CRIT_ERR("addrs needs argument");
1918                 }
1919 #endif /* __linux__ */
1920         END OBJ(tail, 0)
1921                 if (init_tail_object(obj, arg)) {
1922                         obj->type = OBJ_text;
1923                         obj->data.s = strndup("${tail}", text_buffer_size);
1924                 }
1925         END OBJ(head, 0)
1926                 if (init_head_object(obj, arg)) {
1927                         obj->type = OBJ_text;
1928                         obj->data.s = strndup("${head}", text_buffer_size);
1929                 }
1930         END OBJ(lines, 0)
1931                 if (arg) {
1932                         obj->data.s = strndup(arg, text_buffer_size);
1933                 }else{
1934                         CRIT_ERR("lines needs a argument");
1935                 }
1936         END OBJ(words, 0)
1937                 if (arg) {
1938                         obj->data.s = strndup(arg, text_buffer_size);
1939                 }else{
1940                         CRIT_ERR("words needs a argument");
1941                 }
1942         END OBJ(loadavg, INFO_LOADAVG)
1943                 int a = 1, b = 2, c = 3, r = 3;
1944
1945                 if (arg) {
1946                         r = sscanf(arg, "%d %d %d", &a, &b, &c);
1947                         if (r >= 3 && (c < 1 || c > 3)) {
1948                                 r--;
1949                         }
1950                         if (r >= 2 && (b < 1 || b > 3)) {
1951                                 r--, b = c;
1952                         }
1953                         if (r >= 1 && (a < 1 || a > 3)) {
1954                                 r--, a = b, b = c;
1955                         }
1956                 }
1957                 obj->data.loadavg[0] = (r >= 1) ? (unsigned char) a : 0;
1958                 obj->data.loadavg[1] = (r >= 2) ? (unsigned char) b : 0;
1959                 obj->data.loadavg[2] = (r >= 3) ? (unsigned char) c : 0;
1960         END OBJ_IF(if_empty, 0)
1961                 if (!arg) {
1962                         ERR("if_empty needs an argument");
1963                         obj->data.ifblock.s = 0;
1964                 } else {
1965                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
1966                         obj->sub = malloc(sizeof(struct text_object));
1967                         extract_variable_text_internal(obj->sub,
1968                                                        obj->data.ifblock.s, 0);
1969                 }
1970         END OBJ_IF(if_match, 0)
1971                 if (!arg) {
1972                         ERR("if_match needs arguments");
1973                         obj->data.ifblock.s = 0;
1974                 } else {
1975                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
1976                         obj->sub = malloc(sizeof(struct text_object));
1977                         extract_variable_text_internal(obj->sub,
1978                                                        obj->data.ifblock.s, 0);
1979                 }
1980         END OBJ_IF(if_existing, 0)
1981                 if (!arg) {
1982                         ERR("if_existing needs an argument or two");
1983                         obj->data.ifblock.s = NULL;
1984                         obj->data.ifblock.str = NULL;
1985                 } else {
1986                         char buf1[256], buf2[256];
1987                         int r = sscanf(arg, "%255s %255[^\n]", buf1, buf2);
1988
1989                         if (r == 1) {
1990                                 obj->data.ifblock.s = strndup(buf1, text_buffer_size);
1991                                 obj->data.ifblock.str = NULL;
1992                         } else {
1993                                 obj->data.ifblock.s = strndup(buf1, text_buffer_size);
1994                                 obj->data.ifblock.str = strndup(buf2, text_buffer_size);
1995                         }
1996                 }
1997                 DBGP("if_existing: '%s' '%s'", obj->data.ifblock.s, obj->data.ifblock.str);
1998         END OBJ_IF(if_mounted, 0)
1999                 if (!arg) {
2000                         ERR("if_mounted needs an argument");
2001                         obj->data.ifblock.s = 0;
2002                 } else {
2003                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
2004                 }
2005         END OBJ_IF(if_running, 0)
2006                 if (arg) {
2007                         char buf[256];
2008
2009                         snprintf(buf, 256, "pidof %s >/dev/null", arg);
2010                         obj->data.ifblock.s = strndup(buf, text_buffer_size);
2011                 } else {
2012                         ERR("if_running needs an argument");
2013                         obj->data.ifblock.s = 0;
2014                 }
2015         END OBJ(kernel, 0)
2016         END OBJ(machine, 0)
2017         END OBJ(mails, 0)
2018                 float n1;
2019                 char box[256], dst[256];
2020
2021                 if (!arg) {
2022                         n1 = 9.5;
2023                         /* Kapil: Changed from MAIL_FILE to
2024                            current_mail_spool since the latter
2025                            is a copy of the former if undefined
2026                            but the latter should take precedence
2027                            if defined */
2028                         strncpy(box, current_mail_spool, sizeof(box));
2029                 } else {
2030                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2031                                 n1 = 9.5;
2032                                 strncpy(box, arg, sizeof(box));
2033                         }
2034                 }
2035
2036                 variable_substitute(box, dst, sizeof(dst));
2037                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2038                 obj->data.local_mail.interval = n1;
2039         END OBJ(new_mails, 0)
2040                 float n1;
2041                 char box[256], dst[256];
2042
2043                 if (!arg) {
2044                         n1 = 9.5;
2045                         strncpy(box, current_mail_spool, sizeof(box));
2046                 } else {
2047                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2048                                 n1 = 9.5;
2049                                 strncpy(box, arg, sizeof(box));
2050                         }
2051                 }
2052
2053                 variable_substitute(box, dst, sizeof(dst));
2054                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2055                 obj->data.local_mail.interval = n1;
2056         END OBJ(seen_mails, 0)
2057                 float n1;
2058                 char box[256], dst[256];
2059
2060                 if (!arg) {
2061                         n1 = 9.5;
2062                         strncpy(box, current_mail_spool, sizeof(box));
2063                 } else {
2064                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2065                                 n1 = 9.5;
2066                                 strncpy(box, arg, sizeof(box));
2067                         }
2068                 }
2069
2070                 variable_substitute(box, dst, sizeof(dst));
2071                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2072                 obj->data.local_mail.interval = n1;
2073         END OBJ(unseen_mails, 0)
2074                 float n1;
2075                 char box[256], dst[256];
2076
2077                 if (!arg) {
2078                         n1 = 9.5;
2079                         strncpy(box, current_mail_spool, sizeof(box));
2080                 } else {
2081                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2082                                 n1 = 9.5;
2083                                 strncpy(box, arg, sizeof(box));
2084                         }
2085                 }
2086
2087                 variable_substitute(box, dst, sizeof(dst));
2088                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2089                 obj->data.local_mail.interval = n1;
2090         END OBJ(flagged_mails, 0)
2091                 float n1;
2092                 char box[256], dst[256];
2093
2094                 if (!arg) {
2095                         n1 = 9.5;
2096                         strncpy(box, current_mail_spool, sizeof(box));
2097                 } else {
2098                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2099                                 n1 = 9.5;
2100                                 strncpy(box, arg, sizeof(box));
2101                         }
2102                 }
2103
2104                 variable_substitute(box, dst, sizeof(dst));
2105                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2106                 obj->data.local_mail.interval = n1;
2107         END OBJ(unflagged_mails, 0)
2108                 float n1;
2109                 char box[256], dst[256];
2110
2111                 if (!arg) {
2112                         n1 = 9.5;
2113                         strncpy(box, current_mail_spool, sizeof(box));
2114                 } else {
2115                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2116                                 n1 = 9.5;
2117                                 strncpy(box, arg, sizeof(box));
2118                         }
2119                 }
2120
2121                 variable_substitute(box, dst, sizeof(dst));
2122                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2123                 obj->data.local_mail.interval = n1;
2124         END OBJ(forwarded_mails, 0)
2125                 float n1;
2126                 char box[256], dst[256];
2127
2128                 if (!arg) {
2129                         n1 = 9.5;
2130                         strncpy(box, current_mail_spool, sizeof(box));
2131                 } else {
2132                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2133                                 n1 = 9.5;
2134                                 strncpy(box, arg, sizeof(box));
2135                         }
2136                 }
2137
2138                 variable_substitute(box, dst, sizeof(dst));
2139                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2140                 obj->data.local_mail.interval = n1;
2141         END OBJ(unforwarded_mails, 0)
2142                 float n1;
2143                 char box[256], dst[256];
2144
2145                 if (!arg) {
2146                         n1 = 9.5;
2147                         strncpy(box, current_mail_spool, sizeof(box));
2148                 } else {
2149                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2150                                 n1 = 9.5;
2151                                 strncpy(box, arg, sizeof(box));
2152                         }
2153                 }
2154
2155                 variable_substitute(box, dst, sizeof(dst));
2156                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2157                 obj->data.local_mail.interval = n1;
2158         END OBJ(replied_mails, 0)
2159                 float n1;
2160                 char box[256], dst[256];
2161
2162                 if (!arg) {
2163                         n1 = 9.5;
2164                         strncpy(box, current_mail_spool, sizeof(box));
2165                 } else {
2166                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2167                                 n1 = 9.5;
2168                                 strncpy(box, arg, sizeof(box));
2169                         }
2170                 }
2171
2172                 variable_substitute(box, dst, sizeof(dst));
2173                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2174                 obj->data.local_mail.interval = n1;
2175         END OBJ(unreplied_mails, 0)
2176                 float n1;
2177                 char box[256], dst[256];
2178
2179                 if (!arg) {
2180                         n1 = 9.5;
2181                         strncpy(box, current_mail_spool, sizeof(box));
2182                 } else {
2183                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2184                                 n1 = 9.5;
2185                                 strncpy(box, arg, sizeof(box));
2186                         }
2187                 }
2188
2189                 variable_substitute(box, dst, sizeof(dst));
2190                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2191                 obj->data.local_mail.interval = n1;
2192         END OBJ(draft_mails, 0)
2193                 float n1;
2194                 char box[256], dst[256];
2195
2196                 if (!arg) {
2197                         n1 = 9.5;
2198                         strncpy(box, current_mail_spool, sizeof(box));
2199                 } else {
2200                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2201                                 n1 = 9.5;
2202                                 strncpy(box, arg, sizeof(box));
2203                         }
2204                 }
2205
2206                 variable_substitute(box, dst, sizeof(dst));
2207                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2208                 obj->data.local_mail.interval = n1;
2209         END OBJ(trashed_mails, 0)
2210                 float n1;
2211                 char box[256], dst[256];
2212
2213                 if (!arg) {
2214                         n1 = 9.5;
2215                         strncpy(box, current_mail_spool, sizeof(box));
2216                 } else {
2217                         if (sscanf(arg, "%s %f", box, &n1) != 2) {
2218                                 n1 = 9.5;
2219                                 strncpy(box, arg, sizeof(box));
2220                         }
2221                 }
2222
2223                 variable_substitute(box, dst, sizeof(dst));
2224                 obj->data.local_mail.box = strndup(dst, text_buffer_size);
2225                 obj->data.local_mail.interval = n1;
2226         END OBJ(mboxscan, 0)
2227                 obj->data.mboxscan.args = (char *) malloc(text_buffer_size);
2228                 obj->data.mboxscan.output = (char *) malloc(text_buffer_size);
2229                 /* if '1' (in mboxscan.c) then there was SIGUSR1, hmm */
2230                 obj->data.mboxscan.output[0] = 1;
2231                 strncpy(obj->data.mboxscan.args, arg, text_buffer_size);
2232         END OBJ(mem, INFO_MEM)
2233         END OBJ(memeasyfree, INFO_MEM)
2234         END OBJ(memfree, INFO_MEM)
2235         END OBJ(memmax, INFO_MEM)
2236         END OBJ(memperc, INFO_MEM)
2237 #ifdef X11
2238         END OBJ(memgauge, INFO_MEM)
2239                 SIZE_DEFAULTS(gauge);
2240                 scan_gauge(arg, &obj->data.pair.a, &obj->data.pair.b);
2241 #endif /* X11*/
2242         END OBJ(membar, INFO_MEM)
2243                 SIZE_DEFAULTS(bar);
2244                 scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
2245 #ifdef X11
2246         END OBJ(memgraph, INFO_MEM)
2247                 char *buf = 0;
2248                 SIZE_DEFAULTS(graph);
2249                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
2250                                 &obj->e, &obj->char_a, &obj->char_b);
2251
2252                 if (buf) {
2253                         free(buf);
2254                 }
2255 #endif /* X11*/
2256         END OBJ(mixer, INFO_MIXER)
2257                 obj->data.l = mixer_init(arg);
2258         END OBJ(mixerl, INFO_MIXER)
2259                 obj->data.l = mixer_init(arg);
2260         END OBJ(mixerr, INFO_MIXER)
2261                 obj->data.l = mixer_init(arg);
2262 #ifdef X11
2263         END OBJ(mixerbar, INFO_MIXER)
2264                 SIZE_DEFAULTS(bar);
2265                 scan_mixer_bar(arg, &obj->data.mixerbar.l, &obj->data.mixerbar.w,
2266                         &obj->data.mixerbar.h);
2267         END OBJ(mixerlbar, INFO_MIXER)
2268                 SIZE_DEFAULTS(bar);
2269                 scan_mixer_bar(arg, &obj->data.mixerbar.l, &obj->data.mixerbar.w,
2270                         &obj->data.mixerbar.h);
2271         END OBJ(mixerrbar, INFO_MIXER)
2272                 SIZE_DEFAULTS(bar);
2273                 scan_mixer_bar(arg, &obj->data.mixerbar.l, &obj->data.mixerbar.w,
2274                         &obj->data.mixerbar.h);
2275 #endif
2276         END OBJ_IF(if_mixer_mute, INFO_MIXER)
2277                 obj->data.ifblock.i = mixer_init(arg);
2278 #ifdef X11
2279         END OBJ(monitor, INFO_X11)
2280         END OBJ(monitor_number, INFO_X11)
2281 #endif
2282         END OBJ(nodename, 0)
2283         END OBJ(processes, INFO_PROCS)
2284         END OBJ(running_processes, INFO_RUN_PROCS)
2285         END OBJ(shadecolor, 0)
2286 #ifdef X11
2287                 obj->data.l = arg ? get_x11_color(arg) : default_bg_color;
2288 #endif /* X11 */
2289         END OBJ(outlinecolor, 0)
2290 #ifdef X11
2291                 obj->data.l = arg ? get_x11_color(arg) : default_out_color;
2292 #endif /* X11 */
2293         END OBJ(stippled_hr, 0)
2294 #ifdef X11
2295                 int a = stippled_borders, b = 1;
2296
2297                 if (arg) {
2298                         if (sscanf(arg, "%d %d", &a, &b) != 2) {
2299                                 sscanf(arg, "%d", &b);
2300                         }
2301                 }
2302                 if (a <= 0) {
2303                         a = 1;
2304                 }
2305                 obj->data.pair.a = a;
2306                 obj->data.pair.b = b;
2307 #endif /* X11 */
2308         END OBJ(swap, INFO_MEM)
2309         END OBJ(swapmax, INFO_MEM)
2310         END OBJ(swapperc, INFO_MEM)
2311         END OBJ(swapbar, INFO_MEM)
2312                 SIZE_DEFAULTS(bar);
2313                 scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
2314         END OBJ(sysname, 0)
2315         END OBJ(time, 0)
2316                 obj->data.s = strndup(arg ? arg : "%F %T", text_buffer_size);
2317         END OBJ(utime, 0)
2318                 obj->data.s = strndup(arg ? arg : "%F %T", text_buffer_size);
2319         END OBJ(tztime, 0)
2320                 char buf1[256], buf2[256], *fmt, *tz;
2321
2322                 fmt = tz = NULL;
2323                 if (arg) {
2324                         int nArgs = sscanf(arg, "%255s %255[^\n]", buf1, buf2);
2325
2326                         switch (nArgs) {
2327                                 case 2:
2328                                         tz = buf1;
2329                                 case 1:
2330                                         fmt = buf2;
2331                         }
2332                 }
2333
2334                 obj->data.tztime.fmt = strndup(fmt ? fmt : "%F %T", text_buffer_size);
2335                 obj->data.tztime.tz = tz ? strndup(tz, text_buffer_size) : NULL;
2336 #ifdef HAVE_ICONV
2337         END OBJ(iconv_start, 0)
2338                 if (iconv_converting) {
2339                         CRIT_ERR("You must stop your last iconv conversion before "
2340                                 "starting another");
2341                 }
2342                 if (arg) {
2343                         char iconv_from[CODEPAGE_LENGTH];
2344                         char iconv_to[CODEPAGE_LENGTH];
2345
2346                         if (sscanf(arg, "%s %s", iconv_from, iconv_to) != 2) {
2347                                 CRIT_ERR("Invalid arguments for iconv_start");
2348                         } else {
2349                                 iconv_t new_iconv;
2350
2351                                 new_iconv = iconv_open(iconv_to, iconv_from);
2352                                 if (new_iconv == (iconv_t) (-1)) {
2353                                         ERR("Can't convert from %s to %s.", iconv_from, iconv_to);
2354                                 } else {
2355                                         obj->a = register_iconv(&new_iconv);
2356                                         iconv_converting = 1;
2357                                 }
2358                         }
2359                 } else {
2360                         CRIT_ERR("Iconv requires arguments");
2361                 }
2362         END OBJ(iconv_stop, 0)
2363                 iconv_converting = 0;
2364
2365 #endif
2366         END OBJ(totaldown, INFO_NET)
2367                 if (arg) {
2368                         obj->data.net = get_net_stat(arg);
2369                 } else {
2370                         CRIT_ERR("totaldown needs argument");
2371                 }
2372         END OBJ(totalup, INFO_NET)
2373                 obj->data.net = get_net_stat(arg);
2374                 if (arg) {
2375                         obj->data.net = get_net_stat(arg);
2376                 } else {
2377                         CRIT_ERR("totalup needs argument");
2378                 }
2379         END OBJ(updates, 0)
2380         END OBJ_IF(if_updatenr, 0)
2381                 obj->data.ifblock.i = arg ? atoi(arg) : 0;
2382                 if(obj->data.ifblock.i == 0) CRIT_ERR("if_updatenr needs a number above 0 as argument");
2383                 updatereset = obj->data.ifblock.i > updatereset ? obj->data.ifblock.i : updatereset;
2384         END OBJ(alignr, 0)
2385                 obj->data.i = arg ? atoi(arg) : 0;
2386         END OBJ(alignc, 0)
2387                 obj->data.i = arg ? atoi(arg) : 0;
2388         END OBJ(upspeed, INFO_NET)
2389                 if (arg) {
2390                         obj->data.net = get_net_stat(arg);
2391                 } else {
2392                         CRIT_ERR("upspeed needs argument");
2393                 }
2394         END OBJ(upspeedf, INFO_NET)
2395                 if (arg) {
2396                         obj->data.net = get_net_stat(arg);
2397                 } else {
2398                         CRIT_ERR("upspeedf needs argument");
2399                 }
2400
2401 #ifdef X11
2402         END OBJ(upspeedgraph, INFO_NET)
2403                 char *buf = 0;
2404                 SIZE_DEFAULTS(graph);
2405                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
2406                                 &obj->e, &obj->char_a, &obj->char_b);
2407
2408                 // default to DEFAULTNETDEV
2409                 buf = strndup(buf ? buf : "DEFAULTNETDEV", text_buffer_size);
2410                 obj->data.net = get_net_stat(buf);
2411                 free(buf);
2412 #endif
2413         END OBJ(uptime_short, INFO_UPTIME)
2414         END OBJ(uptime, INFO_UPTIME)
2415         END OBJ(user_names, INFO_USERS)
2416         END OBJ(user_times, INFO_USERS)
2417         END OBJ(user_terms, INFO_USERS)
2418         END OBJ(user_number, INFO_USERS)
2419 #if defined(__linux__)
2420         END OBJ(gw_iface, INFO_GW)
2421         END OBJ(gw_ip, INFO_GW)
2422 #endif /* !__linux__ */
2423 #ifndef __OpenBSD__
2424         END OBJ(adt746xcpu, 0)
2425         END OBJ(adt746xfan, 0)
2426 #endif /* !__OpenBSD__ */
2427 #if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \
2428                 || defined(__OpenBSD__)) && (defined(i386) || defined(__i386__))
2429         END OBJ(apm_adapter, 0)
2430         END OBJ(apm_battery_life, 0)
2431         END OBJ(apm_battery_time, 0)
2432 #endif /* __FreeBSD__ */
2433         END OBJ_THREAD(imap_unseen, 0)
2434                 if (arg) {
2435                         // proccss
2436                         obj->data.mail = parse_mail_args(IMAP_TYPE, arg);
2437                         obj->char_b = 0;
2438                 } else {
2439                         obj->char_b = 1;
2440                 }
2441         END OBJ_THREAD(imap_messages, 0)
2442                 if (arg) {
2443                         // proccss
2444                         obj->data.mail = parse_mail_args(IMAP_TYPE, arg);
2445                         obj->char_b = 0;
2446                 } else {
2447                         obj->char_b = 1;
2448                 }
2449         END OBJ_THREAD(pop3_unseen, 0)
2450                 if (arg) {
2451                         // proccss
2452                         obj->data.mail = parse_mail_args(POP3_TYPE, arg);
2453                         obj->char_b = 0;
2454                 } else {
2455                         obj->char_b = 1;
2456                 }
2457         END OBJ_THREAD(pop3_used, 0)
2458                 if (arg) {
2459                         // proccss
2460                         obj->data.mail = parse_mail_args(POP3_TYPE, arg);
2461                         obj->char_b = 0;
2462                 } else {
2463                         obj->char_b = 1;
2464                 }
2465 #ifdef IBM
2466         END OBJ(smapi, 0)
2467                 if (arg)
2468                         obj->data.s = strndup(arg, text_buffer_size);
2469                 else
2470                         ERR("smapi needs an argument");
2471         END OBJ_IF(if_smapi_bat_installed, 0)
2472                 if (!arg) {
2473                         ERR("if_smapi_bat_installed needs an argument");
2474                         obj->data.ifblock.s = 0;
2475                 } else
2476                         obj->data.ifblock.s = strndup(arg, text_buffer_size);
2477         END OBJ(smapi_bat_perc, 0)
2478                 if (arg)
2479                         obj->data.s = strndup(arg, text_buffer_size);
2480                 else
2481                         ERR("smapi_bat_perc needs an argument");
2482         END OBJ(smapi_bat_temp, 0)
2483                 if (arg)
2484                         obj->data.s = strndup(arg, text_buffer_size);
2485                 else
2486                         ERR("smapi_bat_temp needs an argument");
2487         END OBJ(smapi_bat_power, 0)
2488                 if (arg)
2489                         obj->data.s = strndup(arg, text_buffer_size);
2490                 else
2491                         ERR("smapi_bat_power needs an argument");
2492         END OBJ(smapi_bat_bar, 0)
2493                 SIZE_DEFAULTS(bar);
2494                 if(arg) {
2495                         int cnt;
2496                         if(sscanf(arg, "%i %n", &obj->data.i, &cnt) <= 0) {
2497                                 ERR("first argument to smapi_bat_bar must be an integer value");
2498                                 obj->data.i = -1;
2499                         } else {
2500                                 obj->b = 4;
2501                                 arg = scan_bar(arg + cnt, &obj->a, &obj->b);
2502                         }
2503                 } else
2504                         ERR("smapi_bat_bar needs an argument");
2505 #endif /* IBM */
2506 #ifdef MPD
2507 #define mpd_set_maxlen(name) \
2508                 if (arg) { \
2509                         int i; \
2510                         sscanf(arg, "%d", &i); \
2511                         if (i > 0) \
2512                                 obj->data.i = i + 1; \
2513                         else \
2514                                 ERR(#name ": invalid length argument"); \
2515                 }
2516         END OBJ(mpd_artist, INFO_MPD)
2517                 mpd_set_maxlen(mpd_artist);
2518                 init_mpd();
2519         END OBJ(mpd_title, INFO_MPD)
2520                 mpd_set_maxlen(mpd_title);
2521                 init_mpd();
2522         END OBJ(mpd_random, INFO_MPD) init_mpd();
2523         END OBJ(mpd_repeat, INFO_MPD) init_mpd();
2524         END OBJ(mpd_elapsed, INFO_MPD) init_mpd();
2525         END OBJ(mpd_length, INFO_MPD) init_mpd();
2526         END OBJ(mpd_track, INFO_MPD)
2527                 mpd_set_maxlen(mpd_track);
2528                 init_mpd();
2529         END OBJ(mpd_name, INFO_MPD)
2530                 mpd_set_maxlen(mpd_name);
2531                 init_mpd();
2532         END OBJ(mpd_file, INFO_MPD)
2533                 mpd_set_maxlen(mpd_file);
2534                 init_mpd();
2535         END OBJ(mpd_percent, INFO_MPD) init_mpd();
2536         END OBJ(mpd_album, INFO_MPD)
2537                 mpd_set_maxlen(mpd_album);
2538                 init_mpd();
2539         END OBJ(mpd_vol, INFO_MPD) init_mpd();
2540         END OBJ(mpd_bitrate, INFO_MPD) init_mpd();
2541         END OBJ(mpd_status, INFO_MPD) init_mpd();
2542         END OBJ(mpd_bar, INFO_MPD)
2543                 SIZE_DEFAULTS(bar);
2544                 scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
2545                 init_mpd();
2546         END OBJ(mpd_smart, INFO_MPD)
2547                 mpd_set_maxlen(mpd_smart);
2548                 init_mpd();
2549         END OBJ_IF(if_mpd_playing, INFO_MPD)
2550                 init_mpd();
2551 #undef mpd_set_maxlen
2552 #endif /* MPD */
2553 #ifdef MOC
2554         END OBJ(moc_state, INFO_MOC)
2555         END OBJ(moc_file, INFO_MOC)
2556         END OBJ(moc_title, INFO_MOC)
2557         END OBJ(moc_artist, INFO_MOC)
2558         END OBJ(moc_song, INFO_MOC)
2559         END OBJ(moc_album, INFO_MOC)
2560         END OBJ(moc_totaltime, INFO_MOC)
2561         END OBJ(moc_timeleft, INFO_MOC)
2562         END OBJ(moc_curtime, INFO_MOC)
2563         END OBJ(moc_bitrate, INFO_MOC)
2564         END OBJ(moc_rate, INFO_MOC)
2565 #endif /* MOC */
2566 #ifdef XMMS2
2567         END OBJ(xmms2_artist, INFO_XMMS2)
2568         END OBJ(xmms2_album, INFO_XMMS2)
2569         END OBJ(xmms2_title, INFO_XMMS2)
2570         END OBJ(xmms2_genre, INFO_XMMS2)
2571         END OBJ(xmms2_comment, INFO_XMMS2)
2572         END OBJ(xmms2_url, INFO_XMMS2)
2573         END OBJ(xmms2_tracknr, INFO_XMMS2)
2574         END OBJ(xmms2_bitrate, INFO_XMMS2)
2575         END OBJ(xmms2_date, INFO_XMMS2)
2576         END OBJ(xmms2_id, INFO_XMMS2)
2577         END OBJ(xmms2_duration, INFO_XMMS2)
2578         END OBJ(xmms2_elapsed, INFO_XMMS2)
2579         END OBJ(xmms2_size, INFO_XMMS2)
2580         END OBJ(xmms2_status, INFO_XMMS2)
2581         END OBJ(xmms2_percent, INFO_XMMS2)
2582         END OBJ(xmms2_bar, INFO_XMMS2)
2583                 SIZE_DEFAULTS(bar);
2584                 scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
2585         END OBJ(xmms2_smart, INFO_XMMS2)
2586         END OBJ(xmms2_playlist, INFO_XMMS2)
2587         END OBJ(xmms2_timesplayed, INFO_XMMS2)
2588         END OBJ_IF(if_xmms2_connected, INFO_XMMS2)
2589 #endif
2590 #ifdef AUDACIOUS
2591         END OBJ(audacious_status, INFO_AUDACIOUS)
2592         END OBJ(audacious_title, INFO_AUDACIOUS)
2593                 if (arg) {
2594                         sscanf(arg, "%d", &info.audacious.max_title_len);
2595                         if (info.audacious.max_title_len > 0) {
2596                                 info.audacious.max_title_len++;
2597                         } else {
2598                                 CRIT_ERR("audacious_title: invalid length argument");
2599                         }
2600                 }
2601         END OBJ(audacious_length, INFO_AUDACIOUS)
2602         END OBJ(audacious_length_seconds, INFO_AUDACIOUS)
2603         END OBJ(audacious_position, INFO_AUDACIOUS)
2604         END OBJ(audacious_position_seconds, INFO_AUDACIOUS)
2605         END OBJ(audacious_bitrate, INFO_AUDACIOUS)
2606         END OBJ(audacious_frequency, INFO_AUDACIOUS)
2607         END OBJ(audacious_channels, INFO_AUDACIOUS)
2608         END OBJ(audacious_filename, INFO_AUDACIOUS)
2609         END OBJ(audacious_playlist_length, INFO_AUDACIOUS)
2610         END OBJ(audacious_playlist_position, INFO_AUDACIOUS)
2611         END OBJ(audacious_main_volume, INFO_AUDACIOUS)
2612         END OBJ(audacious_bar, INFO_AUDACIOUS)
2613                 SIZE_DEFAULTS(bar);
2614                 scan_bar(arg, &obj->a, &obj->b);
2615 #endif
2616 #ifdef BMPX
2617         END OBJ(bmpx_title, INFO_BMPX)
2618                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
2619         END OBJ(bmpx_artist, INFO_BMPX)
2620                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
2621         END OBJ(bmpx_album, INFO_BMPX)
2622                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
2623         END OBJ(bmpx_track, INFO_BMPX)
2624                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
2625         END OBJ(bmpx_uri, INFO_BMPX)
2626                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
2627         END OBJ(bmpx_bitrate, INFO_BMPX)
2628                 memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
2629 #endif
2630 #ifdef EVE
2631         END OBJ(eve, 0)
2632                 if(arg) {
2633                         int argc;
2634                         char *userid = (char *) malloc(20 * sizeof(char));
2635                         char *apikey = (char *) malloc(64 * sizeof(char));
2636                         char *charid = (char *) malloc(20 * sizeof(char));
2637
2638                         argc = sscanf(arg, "%20s %64s %20s", userid, apikey, charid);
2639                         obj->data.eve.charid = charid;
2640                         obj->data.eve.userid = userid;
2641                         obj->data.eve.apikey = apikey;
2642
2643                         init_eve();
2644                 } else {
2645                         CRIT_ERR("eve needs arguments: <userid> <apikey> <characterid>");
2646                 }
2647 #endif
2648 #ifdef RSS
2649         END OBJ(rss, 0)
2650                 if (arg) {
2651                         int argc, delay, act_par;
2652                         unsigned int nrspaces = 0;
2653                         char *uri = (char *) malloc(128 * sizeof(char));
2654                         char *action = (char *) malloc(64 * sizeof(char));
2655
2656                         argc = sscanf(arg, "%127s %d %63s %d %u", uri, &delay, action,
2657                                         &act_par, &nrspaces);
2658                         obj->data.rss.uri = uri;
2659                         obj->data.rss.delay = delay;
2660                         obj->data.rss.action = action;
2661                         obj->data.rss.act_par = act_par;
2662                         obj->data.rss.nrspaces = nrspaces;
2663
2664                         init_rss_info();
2665                 } else {
2666                         CRIT_ERR("rss needs arguments: <uri> <delay in minutes> <action> "
2667                                         "[act_par] [spaces in front]");
2668                 }
2669 #endif
2670 #ifdef HAVE_LUA
2671         END OBJ(lua, 0)
2672                 if (arg) {
2673                         obj->data.s = strndup(arg, text_buffer_size);
2674                 } else {
2675                         CRIT_ERR("lua needs arguments: <function name> [function parameters]");
2676                 }
2677         END OBJ(lua_parse, 0)
2678                 if (arg) {
2679                         obj->data.s = strndup(arg, text_buffer_size);
2680                 } else {
2681                         CRIT_ERR("lua_parse needs arguments: <function name> [function parameters]");
2682                 }
2683         END OBJ(lua_read_parse, 0)
2684                 if (arg) {
2685                         obj->data.s = strndup(arg, text_buffer_size);
2686                 } else {
2687                         CRIT_ERR("lua_read_parse needs arguments: <function name> <string to pass>");
2688                 }
2689         END OBJ(lua_bar, 0)
2690                 SIZE_DEFAULTS(bar);
2691                 if (arg) {
2692                         arg = scan_bar(arg, &obj->a, &obj->b);
2693                         if(arg) {
2694                                 obj->data.s = strndup(arg, text_buffer_size);
2695                         } else {
2696                                 CRIT_ERR("lua_bar needs arguments: <height>,<width> <function name> [function parameters]");
2697                         }
2698                 } else {
2699                         CRIT_ERR("lua_bar needs arguments: <height>,<width> <function name> [function parameters]");
2700                 }
2701 #ifdef X11
2702         END OBJ(lua_graph, 0)
2703                 SIZE_DEFAULTS(graph);
2704                 if (arg) {
2705                         arg = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
2706                                         &obj->e, &obj->char_a, &obj->char_b);
2707                         if (arg) {
2708                                 obj->data.s = strndup(arg, text_buffer_size);
2709                         } else {
2710                                 CRIT_ERR("lua_graph needs arguments: <\"normal\"|\"log\"> <height>,<width> <gradient colour 1> <gradient colour 2> <scale> <function name> [function parameters]");
2711                         }
2712                 } else {
2713                         CRIT_ERR("lua_graph needs arguments: <\"normal\"|\"log\"> <height>,<width> <gradient colour 1> <gradient colour 2> <scale> <function name> [function parameters]");
2714         }
2715         END OBJ(lua_gauge, 0)
2716                 SIZE_DEFAULTS(gauge);
2717                 if (arg) {
2718                         arg = scan_gauge(arg, &obj->a, &obj->b);
2719                         if (arg) {
2720                                 obj->data.s = strndup(arg, text_buffer_size);
2721                         } else {
2722                                 CRIT_ERR("lua_gauge needs arguments: <height>,<width> <function name> [function parameters]");
2723                         }
2724                 } else {
2725                         CRIT_ERR("lua_gauge needs arguments: <height>,<width> <function name> [function parameters]");
2726                 }
2727 #endif /* X11 */
2728 #endif /* HAVE_LUA */
2729 #ifdef HDDTEMP
2730         END OBJ(hddtemp, 0)
2731                 if (scan_hddtemp(arg, &obj->data.hddtemp.dev,
2732                                  &obj->data.hddtemp.addr, &obj->data.hddtemp.port)) {
2733                         ERR("hddtemp needs arguments");
2734                         obj->type = OBJ_text;
2735                         obj->data.s = strndup("${hddtemp}", text_buffer_size);
2736                         obj->data.hddtemp.update_time = 0;
2737                 } else
2738                         obj->data.hddtemp.temp = NULL;
2739 #endif /* HDDTEMP */
2740 #ifdef TCP_PORT_MONITOR
2741         END OBJ(tcp_portmon, INFO_TCP_PORT_MONITOR)
2742                 tcp_portmon_init(arg, &obj->data.tcp_port_monitor);
2743 #endif /* TCP_PORT_MONITOR */
2744         END OBJ(entropy_avail, INFO_ENTROPY)
2745         END OBJ(entropy_poolsize, INFO_ENTROPY)
2746         END OBJ(entropy_bar, INFO_ENTROPY)
2747                 SIZE_DEFAULTS(bar);
2748                 scan_bar(arg, &obj->a, &obj->b);
2749         END OBJ(scroll, 0)
2750                 int n1, n2;
2751
2752                 obj->data.scroll.step = 1;
2753                 if (arg && sscanf(arg, "%u %n", &obj->data.scroll.show, &n1) > 0) {
2754                         if (sscanf(arg + n1, "%u %n", &obj->data.scroll.step, &n2) > 0)
2755                                 n1 += n2;
2756                         obj->data.scroll.text = strndup(arg + n1, text_buffer_size);
2757                         obj->data.scroll.start = 0;
2758                         obj->sub = malloc(sizeof(struct text_object));
2759                         extract_variable_text_internal(obj->sub,
2760                                         obj->data.scroll.text, 0);
2761                 } else {
2762                         CRIT_ERR("scroll needs arguments: <length> [<step>] <text>");
2763                 }
2764         END OBJ(combine, 0)
2765                 if(arg) {
2766                         unsigned int i,j;
2767                         unsigned int indenting = 0;     //vars can be used as args for other vars
2768                         int startvar[2];
2769                         int endvar[2];
2770                         startvar[0] = endvar[0] = startvar[1] = endvar[1] = -1;
2771                         j=0;
2772                         for(i=0; arg[i] != 0 && j < 2; i++) {
2773                                 if(startvar[j] == -1) {
2774                                         if(arg[i] == '$') {
2775                                                 startvar[j] = i;
2776                                         }
2777                                 }else if(endvar[j] == -1) {
2778                                         if(arg[i] == '{') {
2779                                                 indenting++;
2780                                         }else if(arg[i] == '}') {
2781                                                 indenting--;
2782                                         }
2783                                         if (indenting == 0 && arg[i+1] < 48) {  //<48 has 0, $, and the most used chars not used in varnames but not { or }
2784                                                 endvar[j]=i+1;
2785                                                 j++;
2786                                         }
2787                                 }
2788                         }
2789                         if(startvar[0] >= 0 && endvar[0] >= 0 && startvar[1] >= 0 && endvar[1] >= 0) {
2790                                 obj->data.combine.left=malloc(endvar[0]-startvar[0]+1);
2791                                 obj->data.combine.seperation=malloc(startvar[1]-endvar[0]+1);
2792                                 obj->data.combine.right=malloc(endvar[1]-startvar[1]+1);
2793                                 strncpy(obj->data.combine.left, arg+startvar[0], endvar[0]-startvar[0]); obj->data.combine.left[endvar[0]-startvar[0]]=0;
2794                                 strncpy(obj->data.combine.seperation, arg+endvar[0], startvar[1]-endvar[0]); obj->data.combine.seperation[startvar[1]-endvar[0]]=0;
2795                                 strncpy(obj->data.combine.right, arg+startvar[1], endvar[1]-startvar[1]); obj->data.combine.right[endvar[1]-startvar[1]]=0;
2796                                 obj->sub = malloc(sizeof(struct text_object));
2797                                 extract_variable_text_internal(obj->sub, obj->data.combine.left, 0);
2798                                 obj->sub->sub = malloc(sizeof(struct text_object));
2799                                 extract_variable_text_internal(obj->sub->sub, obj->data.combine.right, 0);
2800                         } else {
2801                                 CRIT_ERR("combine needs arguments: <text1> <text2>");
2802                         }
2803                 } else {
2804                         CRIT_ERR("combine needs arguments: <text1> <text2>");
2805                 }
2806 #ifdef NVIDIA
2807         END OBJ(nvidia, 0)
2808                 if (!arg) {
2809                         CRIT_ERR("nvidia needs an argument\n");
2810                 } else if (set_nvidia_type(&obj->data.nvidia, arg)) {
2811                         CRIT_ERR("nvidia: invalid argument"
2812                                  " specified: '%s'\n", arg);
2813                 }
2814 #endif /* NVIDIA */
2815 #ifdef APCUPSD
2816                 init_apcupsd();
2817                 END OBJ(apcupsd, INFO_APCUPSD)
2818                         if (arg) {
2819                                 char host[64];
2820                                 int port;
2821                                 if (sscanf(arg, "%63s %d", host, &port) != 2) {
2822                                         CRIT_ERR("apcupsd needs arguments: <host> <port>");
2823                                 } else {
2824                                         info.apcupsd.port = htons(port);
2825                                         strncpy(info.apcupsd.host, host, sizeof(info.apcupsd.host));
2826                                 }
2827                         } else {
2828                                 CRIT_ERR("apcupsd needs arguments: <host> <port>");
2829                         }
2830                         END OBJ(apcupsd_name, INFO_APCUPSD)
2831                         END OBJ(apcupsd_model, INFO_APCUPSD)
2832                         END OBJ(apcupsd_upsmode, INFO_APCUPSD)
2833                         END OBJ(apcupsd_cable, INFO_APCUPSD)
2834                         END OBJ(apcupsd_status, INFO_APCUPSD)
2835                         END OBJ(apcupsd_linev, INFO_APCUPSD)
2836                         END OBJ(apcupsd_load, INFO_APCUPSD)
2837                         END OBJ(apcupsd_loadbar, INFO_APCUPSD)
2838                                 SIZE_DEFAULTS(bar);
2839                                 scan_bar(arg, &obj->a, &obj->b);
2840 #ifdef X11
2841                         END OBJ(apcupsd_loadgraph, INFO_APCUPSD)
2842                                 char* buf = 0;
2843                                 SIZE_DEFAULTS(graph);
2844                                 buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
2845                                                 &obj->e, &obj->char_a, &obj->char_b);
2846                                 if (buf) free(buf);
2847                         END OBJ(apcupsd_loadgauge, INFO_APCUPSD)
2848                                 SIZE_DEFAULTS(gauge);
2849                                 scan_gauge(arg, &obj->a, &obj->b);
2850 #endif /* X11 */
2851                         END OBJ(apcupsd_charge, INFO_APCUPSD)
2852                         END OBJ(apcupsd_timeleft, INFO_APCUPSD)
2853                         END OBJ(apcupsd_temp, INFO_APCUPSD)
2854                         END OBJ(apcupsd_lastxfer, INFO_APCUPSD)
2855 #endif /* APCUPSD */
2856         END {
2857                 char buf[256];
2858
2859                 ERR("unknown variable %s", s);
2860                 obj->type = OBJ_text;
2861                 snprintf(buf, 256, "${%s}", s);
2862                 obj->data.s = strndup(buf, text_buffer_size);
2863         }
2864 #undef OBJ
2865
2866         return obj;
2867 }
2868
2869 static struct text_object *create_plain_text(const char *s)
2870 {
2871         struct text_object *obj;
2872
2873         if (s == NULL || *s == '\0') {
2874                 return NULL;
2875         }
2876
2877         obj = new_text_object_internal();
2878
2879         obj->type = OBJ_text;
2880         obj->data.s = strndup(s, text_buffer_size);
2881         return obj;
2882 }
2883
2884 /* backslash_escape - do the actual substitution task for template objects
2885  *
2886  * The field templates is used for substituting the \N occurences. Set it to
2887  * NULL to leave them as they are.
2888  */
2889 static char *backslash_escape(const char *src, char **templates, unsigned int template_count)
2890 {
2891         char *src_dup;
2892         const char *p;
2893         unsigned int dup_idx = 0, dup_len;
2894
2895         dup_len = strlen(src) + 1;
2896         src_dup = malloc(dup_len * sizeof(char));
2897
2898         p = src;
2899         while (*p) {
2900                 switch (*p) {
2901                 case '\\':
2902                         if (!*(p + 1))
2903                                 break;
2904                         if (*(p + 1) == '\\') {
2905                                 src_dup[dup_idx++] = '\\';
2906                                 p++;
2907                         } else if (*(p + 1) == ' ') {
2908                                 src_dup[dup_idx++] = ' ';
2909                                 p++;
2910                         } else if (*(p + 1) == 'n') {
2911                                 src_dup[dup_idx++] = '\n';
2912                                 p++;
2913                         } else if (templates) {
2914                                 unsigned int tmpl_num;
2915                                 int digits;
2916                                 if ((sscanf(p + 1, "%u%n", &tmpl_num, &digits) <= 0) ||
2917                                     (tmpl_num > template_count))
2918                                         break;
2919                                 dup_len += strlen(templates[tmpl_num - 1]);
2920                                 src_dup = realloc(src_dup, dup_len * sizeof(char));
2921                                 sprintf(src_dup + dup_idx, "%s", templates[tmpl_num - 1]);
2922                                 dup_idx += strlen(templates[tmpl_num - 1]);
2923                                 p += digits;
2924                         }
2925                         break;
2926                 default:
2927                         src_dup[dup_idx++] = *p;
2928                         break;
2929                 }
2930                 p++;
2931         }
2932         src_dup[dup_idx] = '\0';
2933         src_dup = realloc(src_dup, (strlen(src_dup) + 1) * sizeof(char));
2934         return src_dup;
2935 }
2936
2937 /* handle_template_object - core logic of the template object
2938  *
2939  * use config variables like this:
2940  * template1 = "$\1\2"
2941  * template2 = "\1: ${fs_bar 4,100 \2} ${fs_used \2} / ${fs_size \2}"
2942  *
2943  * and use them like this:
2944  * ${template1 node name}
2945  * ${template2 root /}
2946  * ${template2 cdrom /mnt/cdrom}
2947  */
2948 static char *handle_template(const char *tmpl, const char *args)
2949 {
2950         char *args_dup = NULL;
2951         char *p, *p_old;
2952         char **argsp = NULL;
2953         unsigned int argcnt = 0, template_idx, i;
2954         char *eval_text;
2955
2956         if ((sscanf(tmpl, "template%u", &template_idx) != 1) ||
2957             (template_idx >= MAX_TEMPLATES))
2958                 return NULL;
2959
2960         if(args) {
2961                 args_dup = strdup(args);
2962                 p = args_dup;
2963                 while (*p) {
2964                         while (*p && (*p == ' ' && *(p - 1) != '\\'))
2965                                 p++;
2966                         if (*(p - 1) == '\\')
2967                                 p--;
2968                         p_old = p;
2969                         while (*p && (*p != ' ' || *(p - 1) == '\\'))
2970                                 p++;
2971                         if (*p) {
2972                                 (*p) = '\0';
2973                                 p++;
2974                         }
2975                         argsp = realloc(argsp, ++argcnt * sizeof(char *));
2976                         argsp[argcnt - 1] = p_old;
2977                 }
2978                 for (i = 0; i < argcnt; i++) {
2979                         char *tmp;
2980                         tmp = backslash_escape(argsp[i], NULL, 0);
2981                         DBGP2("%s: substituted arg '%s' to '%s'", tmpl, argsp[i], tmp);
2982                         argsp[i] = tmp;
2983                 }
2984         }
2985
2986         eval_text = backslash_escape(template[template_idx], argsp, argcnt);
2987         DBGP("substituted %s, output is '%s'", tmpl, eval_text);
2988         free(args_dup);
2989         for (i = 0; i < argcnt; i++)
2990                 free(argsp[i]);
2991         free(argsp);
2992         return eval_text;
2993 }
2994
2995 static char *find_and_replace_templates(const char *inbuf)
2996 {
2997         char *outbuf, *indup, *p, *o, *templ, *args, *tmpl_out;
2998         int stack, outlen;
2999
3000         outlen = strlen(inbuf) + 1;
3001         o = outbuf = calloc(outlen, sizeof(char));
3002         memset(outbuf, 0, outlen * sizeof(char));
3003
3004         p = indup = strdup(inbuf);
3005         while (*p) {
3006                 while (*p && *p != '$')
3007                         *(o++) = *(p++);
3008
3009                 if (!(*p))
3010                         break;
3011
3012                 if (strncmp(p, "$template", 9) && strncmp(p, "${template", 10)) {
3013                         *(o++) = *(p++);
3014                         continue;
3015                 }
3016
3017                 if (*(p + 1) == '{') {
3018                         p += 2;
3019                         templ = p;
3020                         while (*p && !isspace(*p) && *p != '{' && *p != '}')
3021                                 p++;
3022                         if (*p == '}')
3023                                 args = NULL;
3024                         else
3025                                 args = p;
3026
3027                         stack = 1;
3028                         while (*p && stack > 0) {
3029                                 if (*p == '{')
3030                                         stack++;
3031                                 else if (*p == '}')
3032                                         stack--;
3033                                 p++;
3034                         }
3035                         if (stack == 0) {
3036                                 // stack is empty. that means the previous char was }, so we zero it
3037                                 *(p - 1) = '\0';
3038                         } else {
3039                                 // we ran into the end of string without finding a closing }, bark
3040                                 CRIT_ERR("cannot find a closing '}' in template expansion");
3041                         }
3042                 } else {
3043                         templ = p + 1;
3044                         while (*p && !isspace(*p))
3045                                 p++;
3046                         args = NULL;
3047                 }
3048                 tmpl_out = handle_template(templ, args);
3049                 if (tmpl_out) {
3050                         outlen += strlen(tmpl_out);
3051                         outbuf = realloc(outbuf, outlen * sizeof(char));
3052                         strcat (outbuf, tmpl_out);
3053                         free(tmpl_out);
3054                         o = outbuf + strlen(outbuf);
3055                 } else {
3056                         ERR("failed to handle template '%s' with args '%s'", templ, args);
3057                 }
3058         }
3059         *o = '\0';
3060         outbuf = realloc(outbuf, (strlen(outbuf) + 1) * sizeof(char));
3061         free(indup);
3062         return outbuf;
3063 }
3064
3065 static int text_contains_templates(const char *text)
3066 {
3067         if (strcasestr(text, "${template") != NULL)
3068                 return 1;
3069         if (strcasestr(text, "$template") != NULL)
3070                 return 1;
3071         return 0;
3072 }
3073
3074 static void strfold(char *start, int count)
3075 {
3076         char *curplace;
3077         for (curplace = start + count; *curplace != 0; curplace++) {
3078                 *(curplace - count) = *curplace;
3079         }
3080         *(curplace - count - 1) = 0;
3081 }
3082
3083 static size_t remove_comments(char *string)
3084 {
3085         char *curplace, *curplace2;
3086         size_t folded = 0;
3087         for (curplace = string; *curplace != 0; curplace++) {
3088                 if (*curplace == '\\' && *(curplace + 1) == '#') {
3089                         // strcpy can't be used for overlapping strings
3090                         strfold(curplace, 1);
3091                         folded += 1;
3092                 } else if (*curplace == '#') {
3093                         // remove everything until we hit a '\n'
3094                         curplace2 = curplace;
3095                         while (*curplace2) {
3096                                 curplace2++;
3097                                 if (*curplace2 == '\n' &&
3098                                                 *(curplace2 + 1) != '#') break;
3099                         }
3100                         if (*curplace2) {
3101                                 strfold(curplace, curplace2 - curplace);
3102                                 folded += curplace2 - curplace;
3103                         } else {
3104                                 *curplace = 0;
3105                         }
3106                 }
3107         }
3108         return folded;
3109 }
3110
3111 static int extract_variable_text_internal(struct text_object *retval, const char *const_p, char allow_threaded)
3112 {
3113         struct text_object *obj;
3114         char *p, *s, *orig_p;
3115         long line;
3116         void *ifblock_opaque = NULL;
3117         char *tmp_p;
3118         char *arg = 0;
3119         size_t len = 0;
3120
3121         p = strndup(const_p, max_user_text - 1);
3122         while (text_contains_templates(p)) {
3123                 char *tmp;
3124                 tmp = find_and_replace_templates(p);
3125                 free(p);
3126                 p = tmp;
3127         }
3128         s = orig_p = p;
3129
3130         if (strcmp(p, const_p)) {
3131                 DBGP("replaced all templates in text: input is\n'%s'\noutput is\n'%s'", const_p, p);
3132         } else {
3133                 DBGP("no templates to replace");
3134         }
3135
3136         memset(retval, 0, sizeof(struct text_object));
3137
3138         line = global_text_lines;
3139
3140         while (*p) {
3141                 if (*p == '\n') {
3142                         line++;
3143                 }
3144                 if (*p == '$') {
3145                         *p = '\0';
3146                         obj = create_plain_text(s);
3147                         if (obj != NULL) {
3148                                 append_object(retval, obj);
3149                         }
3150                         *p = '$';
3151                         p++;
3152                         s = p;
3153
3154                         if (*p != '$') {
3155                                 char buf[256];
3156                                 const char *var;
3157
3158                                 /* variable is either $foo or ${foo} */
3159                                 if (*p == '{') {
3160                                         unsigned int brl = 1, brr = 0;
3161
3162                                         p++;
3163                                         s = p;
3164                                         while (*p && brl != brr) {
3165                                                 if (*p == '{') {
3166                                                         brl++;
3167                                                 }
3168                                                 if (*p == '}') {
3169                                                         brr++;
3170                                                 }
3171                                                 p++;
3172                                         }
3173                                         p--;
3174                                 } else {
3175                                         s = p;
3176                                         if (*p == '#') {
3177                                                 p++;
3178                                         }
3179                                         while (*p && (isalnum((int) *p) || *p == '_')) {
3180                                                 p++;
3181                                         }
3182                                 }
3183
3184                                 /* copy variable to buffer */
3185                                 len = (p - s > 255) ? 255 : (p - s);
3186                                 strncpy(buf, s, len);
3187                                 buf[len] = '\0';
3188
3189                                 if (*p == '}') {
3190                                         p++;
3191                                 }
3192                                 s = p;
3193
3194                                 /* search for variable in environment */
3195
3196                                 var = getenv(buf);
3197                                 if (var) {
3198                                         obj = create_plain_text(var);
3199                                         if (obj) {
3200                                                 append_object(retval, obj);
3201                                         }
3202                                         continue;
3203                                 }
3204
3205                                 /* if variable wasn't found in environment, use some special */
3206
3207                                 arg = 0;
3208
3209                                 /* split arg */
3210                                 if (strchr(buf, ' ')) {
3211                                         arg = strchr(buf, ' ');
3212                                         *arg = '\0';
3213                                         arg++;
3214                                         while (isspace((int) *arg)) {
3215                                                 arg++;
3216                                         }
3217                                         if (!*arg) {
3218                                                 arg = 0;
3219                                         }
3220                                 }
3221
3222                                 /* lowercase variable name */
3223                                 tmp_p = buf;
3224                                 while (*tmp_p) {
3225                                         *tmp_p = tolower(*tmp_p);
3226                                         tmp_p++;
3227                                 }
3228
3229                                 obj = construct_text_object(buf, arg,
3230                                                 line, allow_threaded,
3231                                                 &ifblock_opaque);
3232                                 if (obj != NULL) {
3233                                         append_object(retval, obj);
3234                                 }
3235                                 continue;
3236                         } else {
3237                                 obj = create_plain_text("$");
3238                                 s = p + 1;
3239                                 if (obj != NULL) {
3240                                         append_object(retval, obj);
3241                                 }
3242                         }
3243                 } else if (*p == '#') {
3244                         remove_comments(p);
3245                 }
3246                 p++;
3247         }
3248         remove_comments(s);
3249         obj = create_plain_text(s);
3250         if (obj != NULL) {
3251                 append_object(retval, obj);
3252         }
3253
3254         if (!ifblock_stack_empty(&ifblock_opaque)) {
3255                 ERR("one or more $endif's are missing");
3256         }
3257
3258         free(orig_p);
3259         return 0;
3260 }
3261
3262 static void extract_variable_text(const char *p)
3263 {
3264         free_text_objects(&global_root_object, 0);
3265         if (tmpstring1) {
3266                 free(tmpstring1);
3267                 tmpstring1 = 0;
3268         }
3269         if (tmpstring2) {
3270                 free(tmpstring2);
3271                 tmpstring2 = 0;
3272         }
3273         if (text_buffer) {
3274                 free(text_buffer);
3275                 text_buffer = 0;
3276         }
3277
3278         extract_variable_text_internal(&global_root_object, p, 1);
3279 }
3280
3281 int parse_conky_vars(struct text_object *root, char *txt, char *p, struct information *cur)
3282 {
3283         extract_variable_text_internal(root, txt, 0);
3284         generate_text_internal(p, max_user_text, *root, cur);
3285         return 0;
3286 }
3287
3288 static inline struct mail_s *ensure_mail_thread(struct text_object *obj,
3289                 void *thread(void *), const char *text)
3290 {
3291         if (obj->char_b && info.mail) {
3292                 // this means we use info
3293                 if (!info.mail->p_timed_thread) {
3294                         info.mail->p_timed_thread =
3295                                 timed_thread_create(thread,
3296                                                 (void *) info.mail, info.mail->interval * 1000000);
3297                         if (!info.mail->p_timed_thread) {
3298                                 ERR("Error creating %s timed thread", text);
3299                         }
3300                         timed_thread_register(info.mail->p_timed_thread,
3301                                         &info.mail->p_timed_thread);
3302                         if (timed_thread_run(info.mail->p_timed_thread)) {
3303                                 ERR("Error running %s timed thread", text);
3304                         }
3305                 }
3306                 return info.mail;
3307         } else if (obj->data.mail) {
3308                 // this means we use obj
3309                 if (!obj->data.mail->p_timed_thread) {
3310                         obj->data.mail->p_timed_thread =
3311                                 timed_thread_create(thread,
3312                                                 (void *) obj->data.mail,
3313                                                 obj->data.mail->interval * 1000000);
3314                         if (!obj->data.mail->p_timed_thread) {
3315                                 ERR("Error creating %s timed thread", text);
3316                         }
3317                         timed_thread_register(obj->data.mail->p_timed_thread,
3318                                         &obj->data.mail->p_timed_thread);
3319                         if (timed_thread_run(obj->data.mail->p_timed_thread)) {
3320                                 ERR("Error running %s timed thread", text);
3321                         }
3322                 }
3323                 return obj->data.mail;
3324         } else if (!obj->a) {
3325                 // something is wrong, warn once then stop
3326                 ERR("There's a problem with your mail settings.  "
3327                                 "Check that the global mail settings are properly defined"
3328                                 " (line %li).", obj->line);
3329                 obj->a++;
3330         }
3331         return NULL;
3332 }
3333
3334 char *format_time(unsigned long timeval, const int width)
3335 {
3336         char buf[10];
3337         unsigned long nt;       // narrow time, for speed on 32-bit
3338         unsigned cc;            // centiseconds
3339         unsigned nn;            // multi-purpose whatever
3340
3341         nt = timeval;
3342         cc = nt % 100;          // centiseconds past second
3343         nt /= 100;                      // total seconds
3344         nn = nt % 60;           // seconds past the minute
3345         nt /= 60;                       // total minutes
3346         if (width >= snprintf(buf, sizeof buf, "%lu:%02u.%02u",
3347                                 nt, nn, cc)) {
3348                 return strndup(buf, text_buffer_size);
3349         }
3350         if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn)) {
3351                 return strndup(buf, text_buffer_size);
3352         }
3353         nn = nt % 60;           // minutes past the hour
3354         nt /= 60;                       // total hours
3355         if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn)) {
3356                 return strndup(buf, text_buffer_size);
3357         }
3358         nn = nt;                        // now also hours
3359         if (width >= snprintf(buf, sizeof buf, "%uh", nn)) {
3360                 return strndup(buf, text_buffer_size);
3361         }
3362         nn /= 24;                       // now days
3363         if (width >= snprintf(buf, sizeof buf, "%ud", nn)) {
3364                 return strndup(buf, text_buffer_size);
3365         }
3366         nn /= 7;                        // now weeks
3367         if (width >= snprintf(buf, sizeof buf, "%uw", nn)) {
3368                 return strndup(buf, text_buffer_size);
3369         }
3370         // well shoot, this outta' fit...
3371         return strndup("<inf>", text_buffer_size);
3372 }
3373
3374 //remove backspaced chars, example: "dog^H^H^Hcat" becomes "cat"
3375 //string has to end with \0 and it's length should fit in a int
3376 #define BACKSPACE 8
3377 void remove_deleted_chars(char *string){
3378         int i = 0;
3379         while(string[i] != 0){
3380                 if(string[i] == BACKSPACE){
3381                         if(i != 0){
3382                                 strcpy( &(string[i-1]), &(string[i+1]) );
3383                                 i--;
3384                         }else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
3385                 }else i++;
3386         }
3387 }
3388
3389 static inline void format_media_player_time(char *buf, const int size,
3390                 int seconds)
3391 {
3392         int days, hours, minutes;
3393
3394         days = seconds / (24 * 60 * 60);
3395         seconds %= (24 * 60 * 60);
3396         hours = seconds / (60 * 60);
3397         seconds %= (60 * 60);
3398         minutes = seconds / 60;
3399         seconds %= 60;
3400
3401         if (days > 0) {
3402                 snprintf(buf, size, "%i days %i:%02i:%02i", days,
3403                                 hours, minutes, seconds);
3404         } else if (hours > 0) {
3405                 snprintf(buf, size, "%i:%02i:%02i", hours, minutes,
3406                                 seconds);
3407         } else {
3408                 snprintf(buf, size, "%i:%02i", minutes, seconds);
3409         }
3410 }
3411
3412 static inline double get_barnum(char *buf)
3413 {
3414         char *c = buf;
3415         double barnum;
3416
3417         while (*c) {
3418                 if (*c == '\001') {
3419                         *c = ' ';
3420                 }
3421                 c++;
3422         }
3423
3424         if (sscanf(buf, "%lf", &barnum) == 0) {
3425                 ERR("reading exec value failed (perhaps it's not the "
3426                                 "correct format?)");
3427                 return -1;
3428         }
3429         if (barnum > 100.0 || barnum < 0.0) {
3430                 ERR("your exec value is not between 0 and 100, "
3431                                 "therefore it will be ignored");
3432                 return -1;
3433         }
3434         return barnum;
3435 }
3436
3437 static void generate_text_internal(char *p, int p_max_size,
3438                 struct text_object root, struct information *cur)
3439 {
3440         struct text_object *obj;
3441
3442         /* for the OBJ_top* handler */
3443         struct process **needed = 0;
3444
3445 #ifdef HAVE_ICONV
3446         char buff_in[p_max_size];
3447         buff_in[0] = 0;
3448         iconv_converting = 0;
3449 #endif
3450
3451         p[0] = 0;
3452         obj = root.next;
3453         while (obj && p_max_size > 0) {
3454                 needed = 0; // reset for top stuff
3455
3456 /* IFBLOCK jumping algorithm
3457  *
3458  * This is easier as it looks like:
3459  * - each IF checks it's condition
3460  *   - on FALSE: call DO_JUMP
3461  *   - on TRUE: don't care
3462  * - each ELSE calls DO_JUMP unconditionally
3463  * - each ENDIF is silently being ignored
3464  *
3465  * Why this works:
3466  * DO_JUMP overwrites the "obj" variable of the loop and sets it to the target
3467  * (i.e. the corresponding ELSE or ENDIF). After that, processing for the given
3468  * object can continue, free()ing stuff e.g., then the for-loop does the rest: as
3469  * regularly, "obj" is being updated to point to obj->next, so object parsing
3470  * continues right after the corresponding ELSE or ENDIF. This means that if we
3471  * find an ELSE, it's corresponding IF must not have jumped, so we need to jump
3472  * always. If we encounter an ENDIF, it's corresponding IF or ELSE has not
3473  * jumped, and there is nothing to do.
3474  */
3475 #define DO_JUMP { \
3476         DBGP2("jumping"); \
3477         obj = obj->data.ifblock.next; \
3478 }
3479
3480 #define OBJ(a) break; case OBJ_##a:
3481
3482                 switch (obj->type) {
3483                         default:
3484                                 ERR("not implemented obj type %d", obj->type);
3485 #ifndef __OpenBSD__
3486                         OBJ(acpitemp) {
3487                                 temp_print(p, p_max_size, get_acpi_temperature(obj->data.i), TEMP_CELSIUS);
3488                         }
3489 #endif /* !__OpenBSD__ */
3490                         OBJ(freq) {
3491                                 if (obj->a) {
3492                                         obj->a = get_freq(p, p_max_size, "%.0f", 1,
3493                                                         obj->data.cpu_index);
3494                                 }
3495                         }
3496                         OBJ(freq_g) {
3497                                 if (obj->a) {
3498 #ifndef __OpenBSD__
3499                                         obj->a = get_freq(p, p_max_size, "%'.2f", 1000,
3500                                                         obj->data.cpu_index);
3501 #else
3502                                         /* OpenBSD has no such flag (SUSv2) */
3503                                         obj->a = get_freq(p, p_max_size, "%.2f", 1000,
3504                                                         obj->data.cpu_index);
3505 #endif
3506                                 }
3507                         }
3508 #if defined(__linux__)
3509                         OBJ(voltage_mv) {
3510                                 if (obj->a) {
3511                                         obj->a = get_voltage(p, p_max_size, "%.0f", 1,
3512                                                         obj->data.cpu_index);
3513                                 }
3514                         }
3515                         OBJ(voltage_v) {
3516                                 if (obj->a) {
3517                                         obj->a = get_voltage(p, p_max_size, "%'.3f", 1000,
3518                                                         obj->data.cpu_index);
3519                                 }
3520                         }
3521
3522 #ifdef HAVE_IWLIB
3523                         OBJ(wireless_essid) {
3524                                 snprintf(p, p_max_size, "%s", obj->data.net->essid);
3525                         }
3526                         OBJ(wireless_mode) {
3527                                 snprintf(p, p_max_size, "%s", obj->data.net->mode);
3528                         }
3529                         OBJ(wireless_bitrate) {
3530                                 snprintf(p, p_max_size, "%s", obj->data.net->bitrate);
3531                         }
3532                         OBJ(wireless_ap) {
3533                                 snprintf(p, p_max_size, "%s", obj->data.net->ap);
3534                         }
3535                         OBJ(wireless_link_qual) {
3536                                 spaced_print(p, p_max_size, "%d", 4,
3537                                                 obj->data.net->link_qual);
3538                         }
3539                         OBJ(wireless_link_qual_max) {
3540                                 spaced_print(p, p_max_size, "%d", 4,
3541                                                 obj->data.net->link_qual_max);
3542                         }
3543                         OBJ(wireless_link_qual_perc) {
3544                                 if (obj->data.net->link_qual_max > 0) {
3545                                         spaced_print(p, p_max_size, "%.0f", 5,
3546                                                         (double) obj->data.net->link_qual /
3547                                                         obj->data.net->link_qual_max * 100);
3548                                 } else {
3549                                         spaced_print(p, p_max_size, "unk", 5);
3550                                 }
3551                         }
3552                         OBJ(wireless_link_bar) {
3553                                 new_bar(p, obj->a, obj->b, ((double) obj->data.net->link_qual /
3554                                                         obj->data.net->link_qual_max) * 255.0);
3555                         }
3556 #endif /* HAVE_IWLIB */
3557
3558 #endif /* __linux__ */
3559
3560 #ifndef __OpenBSD__
3561                         OBJ(adt746xcpu) {
3562                                 get_adt746x_cpu(p, p_max_size);
3563                         }
3564                         OBJ(adt746xfan) {
3565                                 get_adt746x_fan(p, p_max_size);
3566                         }
3567                         OBJ(acpifan) {
3568                                 get_acpi_fan(p, p_max_size);
3569                         }
3570                         OBJ(acpiacadapter) {
3571                                 get_acpi_ac_adapter(p, p_max_size);
3572                         }
3573                         OBJ(battery) {
3574                                 get_battery_stuff(p, p_max_size, obj->data.s, BATTERY_STATUS);
3575                         }
3576                         OBJ(battery_time) {
3577                                 get_battery_stuff(p, p_max_size, obj->data.s, BATTERY_TIME);
3578                         }
3579                         OBJ(battery_percent) {
3580                                 percent_print(p, p_max_size, get_battery_perct(obj->data.s));
3581                         }
3582                         OBJ(battery_bar) {
3583 #ifdef X11
3584                                 if(output_methods & TO_X) {
3585                                         new_bar(p, obj->a, obj->b, get_battery_perct_bar(obj->data.s));
3586                                 }else{
3587 #endif /* X11 */
3588                                         if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
3589                                         new_bar_in_shell(p, p_max_size, get_battery_perct_bar(obj->data.s) / 2.55, obj->a);
3590 #ifdef X11
3591                                 }
3592 #endif /* X11 */
3593                         }
3594                         OBJ(battery_short) {
3595                                 get_battery_short_status(p, p_max_size, obj->data.s);
3596                         }
3597 #endif /* __OpenBSD__ */
3598
3599                         OBJ(buffers) {
3600                                 human_readable(cur->buffers * 1024, p, 255);
3601                         }
3602                         OBJ(cached) {
3603                                 human_readable(cur->cached * 1024, p, 255);
3604                         }
3605                         OBJ(cpu) {
3606                                 if (obj->data.cpu_index > info.cpu_count) {
3607                                         ERR("obj->data.cpu_index %i info.cpu_count %i",
3608                                                         obj->data.cpu_index, info.cpu_count);
3609                                         CRIT_ERR("attempting to use more CPUs than you have!");
3610                                 }
3611                                 percent_print(p, p_max_size,
3612                                               round_to_int(cur->cpu_usage[obj->data.cpu_index] * 100.0));
3613                         }
3614 #ifdef X11
3615                         OBJ(cpugauge)
3616                                 new_gauge(p, obj->a, obj->b,
3617                                                 round_to_int(cur->cpu_usage[obj->data.cpu_index] * 255.0));
3618 #endif /* X11 */
3619                         OBJ(cpubar) {
3620 #ifdef X11
3621                                 if(output_methods & TO_X) {
3622                                         new_bar(p, obj->a, obj->b,
3623                                                 round_to_int(cur->cpu_usage[obj->data.cpu_index] * 255.0));
3624                                 }else{
3625 #endif /* X11 */
3626                                         if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
3627                                         new_bar_in_shell(p, p_max_size, round_to_int(cur->cpu_usage[obj->data.cpu_index] * 100), obj->a);
3628 #ifdef X11
3629                                 }
3630 #endif /* X11 */
3631                         }
3632 #ifdef X11
3633                         OBJ(cpugraph) {
3634                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
3635                                                 round_to_int(cur->cpu_usage[obj->data.cpu_index] * 100),
3636                                                 100, 1, obj->char_a, obj->char_b);
3637                         }
3638                         OBJ(loadgraph) {
3639                                 new_graph(p, obj->a, obj->b, obj->c, obj->d, cur->loadavg[0],
3640                                                 obj->e, 1, obj->char_a, obj->char_b);
3641                         }
3642                         OBJ(color) {
3643                                 new_fg(p, obj->data.l);
3644                         }
3645                         OBJ(color0) {
3646                                 new_fg(p, color0);
3647                         }
3648                         OBJ(color1) {
3649                                 new_fg(p, color1);
3650                         }
3651                         OBJ(color2) {
3652                                 new_fg(p, color2);
3653                         }
3654                         OBJ(color3) {
3655                                 new_fg(p, color3);
3656                         }
3657                         OBJ(color4) {
3658                                 new_fg(p, color4);
3659                         }
3660                         OBJ(color5) {
3661                                 new_fg(p, color5);
3662                         }
3663                         OBJ(color6) {
3664                                 new_fg(p, color6);
3665                         }
3666                         OBJ(color7) {
3667                                 new_fg(p, color7);
3668                         }
3669                         OBJ(color8) {
3670                                 new_fg(p, color8);
3671                         }
3672                         OBJ(color9) {
3673                                 new_fg(p, color9);
3674                         }
3675 #endif /* X11 */
3676                         OBJ(conky_version) {
3677                                 snprintf(p, p_max_size, "%s", VERSION);
3678                         }
3679                         OBJ(conky_build_date) {
3680                                 snprintf(p, p_max_size, "%s", BUILD_DATE);
3681                         }
3682                         OBJ(conky_build_arch) {
3683                                 snprintf(p, p_max_size, "%s", BUILD_ARCH);
3684                         }
3685 #if defined(__linux__)
3686                         OBJ(disk_protect) {
3687                                 snprintf(p, p_max_size, "%s",
3688                                                 get_disk_protect_queue(obj->data.s));
3689                         }
3690                         OBJ(i8k_version) {
3691                                 snprintf(p, p_max_size, "%s", i8k.version);
3692                         }
3693                         OBJ(i8k_bios) {
3694                                 snprintf(p, p_max_size, "%s", i8k.bios);
3695                         }
3696                         OBJ(i8k_serial) {
3697                                 snprintf(p, p_max_size, "%s", i8k.serial);
3698                         }
3699                         OBJ(i8k_cpu_temp) {
3700                                 int cpu_temp;
3701
3702                                 sscanf(i8k.cpu_temp, "%d", &cpu_temp);
3703                                 temp_print(p, p_max_size, (double)cpu_temp, TEMP_CELSIUS);
3704                         }
3705                         OBJ(i8k_left_fan_status) {
3706                                 int left_fan_status;
3707
3708                                 sscanf(i8k.left_fan_status, "%d", &left_fan_status);
3709                                 if (left_fan_status == 0) {
3710                                         snprintf(p, p_max_size, "off");
3711                                 }
3712                                 if (left_fan_status == 1) {
3713                                         snprintf(p, p_max_size, "low");
3714                                 }
3715                                 if (left_fan_status == 2) {
3716                                         snprintf(p, p_max_size, "high");
3717                                 }
3718                         }
3719                         OBJ(i8k_right_fan_status) {
3720                                 int right_fan_status;
3721
3722                                 sscanf(i8k.right_fan_status, "%d", &right_fan_status);
3723                                 if (right_fan_status == 0) {
3724                                         snprintf(p, p_max_size, "off");
3725                                 }
3726                                 if (right_fan_status == 1) {
3727                                         snprintf(p, p_max_size, "low");
3728                                 }
3729                                 if (right_fan_status == 2) {
3730                                         snprintf(p, p_max_size, "high");
3731                                 }
3732                         }
3733                         OBJ(i8k_left_fan_rpm) {
3734                                 snprintf(p, p_max_size, "%s", i8k.left_fan_rpm);
3735                         }
3736                         OBJ(i8k_right_fan_rpm) {
3737                                 snprintf(p, p_max_size, "%s", i8k.right_fan_rpm);
3738                         }
3739                         OBJ(i8k_ac_status) {
3740                                 int ac_status;
3741
3742                                 sscanf(i8k.ac_status, "%d", &ac_status);
3743                                 if (ac_status == -1) {
3744                                         snprintf(p, p_max_size, "disabled (read i8k docs)");
3745                                 }
3746                                 if (ac_status == 0) {
3747                                         snprintf(p, p_max_size, "off");
3748                                 }
3749                                 if (ac_status == 1) {
3750                                         snprintf(p, p_max_size, "on");
3751                                 }
3752                         }
3753                         OBJ(i8k_buttons_status) {
3754                                 snprintf(p, p_max_size, "%s", i8k.buttons_status);
3755                         }
3756 #if defined(IBM)
3757                         OBJ(ibm_fan) {
3758                                 get_ibm_acpi_fan(p, p_max_size);
3759                         }
3760                         OBJ(ibm_temps) {
3761                                 get_ibm_acpi_temps();
3762                                 temp_print(p, p_max_size,
3763                                            ibm_acpi.temps[obj->data.sensor], TEMP_CELSIUS);
3764                         }
3765                         OBJ(ibm_volume) {
3766                                 get_ibm_acpi_volume(p, p_max_size);
3767                         }
3768                         OBJ(ibm_brightness) {
3769                                 get_ibm_acpi_brightness(p, p_max_size);
3770                         }
3771 #endif /* IBM */
3772                         /* information from sony_laptop kernel module
3773                          * /sys/devices/platform/sony-laptop */
3774                         OBJ(sony_fanspeed) {
3775                                 get_sony_fanspeed(p, p_max_size);
3776                         }
3777                         OBJ(if_gw) {
3778                                 if (!cur->gw_info.count) {
3779                                         DO_JUMP;
3780                                 }
3781                         }
3782                         OBJ(gw_iface) {
3783                                 snprintf(p, p_max_size, "%s", cur->gw_info.iface);
3784                         }
3785                         OBJ(gw_ip) {
3786                                 snprintf(p, p_max_size, "%s", cur->gw_info.ip);
3787                         }
3788                         OBJ(laptop_mode) {
3789                                 snprintf(p, p_max_size, "%d", get_laptop_mode());
3790                         }
3791                         OBJ(pb_battery) {
3792                                 get_powerbook_batt_info(p, p_max_size, obj->data.i);
3793                         }
3794 #endif /* __linux__ */
3795 #if (defined(__FreeBSD__) || defined(__linux__))
3796                         OBJ(if_up) {
3797                                 if ((obj->data.ifblock.s)
3798                                                 && (!interface_up(obj->data.ifblock.s))) {
3799                                         DO_JUMP;
3800                                 }
3801                         }
3802 #endif
3803 #ifdef __OpenBSD__
3804                         OBJ(obsd_sensors_temp) {
3805                                 obsd_sensors.device = sensor_device;
3806                                 update_obsd_sensors();
3807                                 temp_print(p, p_max_size,
3808                                            obsd_sensors.temp[obsd_sensors.device][obj->data.sensor],
3809                                            TEMP_CELSIUS);
3810                         }
3811                         OBJ(obsd_sensors_fan) {
3812                                 obsd_sensors.device = sensor_device;
3813                                 update_obsd_sensors();
3814                                 snprintf(p, p_max_size, "%d",
3815                                                 obsd_sensors.fan[obsd_sensors.device][obj->data.sensor]);
3816                         }
3817                         OBJ(obsd_sensors_volt) {
3818                                 obsd_sensors.device = sensor_device;
3819                                 update_obsd_sensors();
3820                                 snprintf(p, p_max_size, "%.2f",
3821                                                 obsd_sensors.volt[obsd_sensors.device][obj->data.sensor]);
3822                         }
3823                         OBJ(obsd_vendor) {
3824                                 get_obsd_vendor(p, p_max_size);
3825                         }
3826                         OBJ(obsd_product) {
3827                                 get_obsd_product(p, p_max_size);
3828                         }
3829 #endif /* __OpenBSD__ */
3830 #ifdef X11
3831                         OBJ(font) {
3832                                 new_font(p, obj->data.s);
3833                         }
3834 #endif /* X11 */
3835                         /* TODO: move this correction from kB to kB/s elsewhere
3836                          * (or get rid of it??) */
3837                         OBJ(diskio) {
3838                                 human_readable((obj->data.diskio->current / update_interval) * 1024LL,
3839                                                 p, p_max_size);
3840                         }
3841                         OBJ(diskio_write) {
3842                                 human_readable((obj->data.diskio->current_write / update_interval) * 1024LL,
3843                                                 p, p_max_size);
3844                         }
3845                         OBJ(diskio_read) {
3846                                 human_readable((obj->data.diskio->current_read / update_interval) * 1024LL,
3847                                                 p, p_max_size);
3848                         }
3849 #ifdef X11
3850                         OBJ(diskiograph) {
3851                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
3852                                           obj->data.diskio->current, obj->e, 1, obj->char_a, obj->char_b);
3853                         }
3854                         OBJ(diskiograph_read) {
3855                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
3856                                           obj->data.diskio->current_read, obj->e, 1, obj->char_a, obj->char_b);
3857                         }
3858                         OBJ(diskiograph_write) {
3859                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
3860                                           obj->data.diskio->current_write, obj->e, 1, obj->char_a, obj->char_b);
3861                         }
3862 #endif /* X11 */
3863                         OBJ(downspeed) {
3864                                 human_readable(obj->data.net->recv_speed, p, 255);
3865                         }
3866                         OBJ(downspeedf) {
3867                                 spaced_print(p, p_max_size, "%.1f", 8,
3868                                                 obj->data.net->recv_speed / 1024.0);
3869                         }
3870 #ifdef X11
3871                         OBJ(downspeedgraph) {
3872                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
3873                                         obj->data.net->recv_speed / 1024.0, obj->e, 1, obj->char_a, obj->char_b);
3874                         }
3875 #endif /* X11 */
3876                         OBJ(else) {
3877                                 /* Since we see you, you're if has not jumped.
3878                                  * Do Ninja jump here: without leaving traces.
3879                                  * This is to prevent us from stale jumped flags.
3880                                  */
3881                                 obj = obj->data.ifblock.next;
3882                                 continue;
3883                         }
3884                         OBJ(endif) {
3885                                 /* harmless object, just ignore */
3886                         }
3887 #ifdef HAVE_POPEN
3888                         OBJ(addr) {
3889                                 if ((obj->data.net->addr.sa_data[2] & 255) == 0
3890                                                 && (obj->data.net->addr.sa_data[3] & 255) == 0
3891                                                 && (obj->data.net->addr.sa_data[4] & 255) == 0
3892                                                 && (obj->data.net->addr.sa_data[5] & 255) == 0) {
3893                                         snprintf(p, p_max_size, "No Address");
3894                                 } else {
3895                                         snprintf(p, p_max_size, "%u.%u.%u.%u",
3896                                                 obj->data.net->addr.sa_data[2] & 255,
3897                                                 obj->data.net->addr.sa_data[3] & 255,
3898                                                 obj->data.net->addr.sa_data[4] & 255,
3899                                                 obj->data.net->addr.sa_data[5] & 255);
3900                                 }
3901                         }
3902 #if defined(__linux__)
3903                         OBJ(addrs) {
3904                                 if(NULL != obj->data.net->addrs && strlen(obj->data.net->addrs) > 2)
3905                                 {
3906                                         obj->data.net->addrs[strlen(obj->data.net->addrs) - 2] = 0; /* remove ", " from end of string */
3907                                         strcpy(p, obj->data.net->addrs);
3908                                 }
3909                                 else
3910                                         strcpy(p, "0.0.0.0");
3911            }
3912 #endif /* __linux__ */
3913 #if defined(IMLIB2) && defined(X11)
3914                         OBJ(image) {
3915                                 /* doesn't actually draw anything, just queues it omp.  the
3916                                  * image will get drawn after the X event loop */
3917                                 cimlib_add_image(obj->data.s);
3918                         }
3919 #endif /* IMLIB2 */
3920                         OBJ(eval) {
3921                                 struct information *tmp_info;
3922                                 struct text_object subroot, subroot2;
3923
3924                                 tmp_info = malloc(sizeof(struct information));
3925                                 memcpy(tmp_info, cur, sizeof(struct information));
3926                                 parse_conky_vars(&subroot, obj->data.s, p, tmp_info);
3927                                 DBGP("evaluated '%s' to '%s'", obj->data.s, p);
3928                                 parse_conky_vars(&subroot2, p, p, tmp_info);
3929
3930                                 free_text_objects(&subroot, 1);
3931                                 free_text_objects(&subroot2, 1);
3932                                 free(tmp_info);
3933                         }
3934                         OBJ(exec) {
3935                                 read_exec(obj->data.s, p, text_buffer_size);
3936                                 remove_deleted_chars(p);
3937                         }
3938                         OBJ(execp) {
3939                                 struct information *tmp_info;
3940                                 struct text_object subroot;
3941
3942                                 read_exec(obj->data.s, p, text_buffer_size);
3943
3944                                 tmp_info = malloc(sizeof(struct information));
3945                                 memcpy(tmp_info, cur, sizeof(struct information));
3946                                 parse_conky_vars(&subroot, p, p, tmp_info);
3947
3948                                 free_text_objects(&subroot, 1);
3949                                 free(tmp_info);
3950                         }
3951 #ifdef X11
3952                         OBJ(execgauge) {
3953                                 double barnum;
3954
3955                                 read_exec(obj->data.s, p, text_buffer_size);
3956                                 barnum = get_barnum(p); /*using the same function*/
3957
3958                                 if (barnum >= 0.0) {
3959                                         barnum /= 100;
3960                                         new_gauge(p, obj->a, obj->b, round_to_int(barnum * 255.0));
3961                                 }
3962                         }
3963 #endif /* X11 */
3964                         OBJ(execbar) {
3965                                 double barnum;
3966
3967                                 read_exec(obj->data.s, p, text_buffer_size);
3968                                 barnum = get_barnum(p);
3969
3970                                 if (barnum >= 0.0) {
3971 #ifdef X11
3972                                         if(output_methods & TO_X) {
3973                                                 barnum /= 100;
3974                                                 new_bar(p, obj->a, obj->b, round_to_int(barnum * 255.0));
3975                                         }else{
3976 #endif /* X11 */
3977                                                 if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
3978                                                 new_bar_in_shell(p, p_max_size, barnum, obj->a);
3979 #ifdef X11
3980                                         }
3981 #endif /* X11 */
3982                                 }
3983                         }
3984 #ifdef X11
3985                         OBJ(execgraph) {
3986                                 char showaslog = FALSE;
3987                                 char tempgrad = FALSE;
3988                                 double barnum;
3989                                 char *cmd = obj->data.s;
3990
3991                                 if (strncasecmp(obj->data.execi.cmd, LOGGRAPH" ", strlen(LOGGRAPH" ")) == EQUAL) {
3992                                         showaslog = TRUE;
3993                                         cmd = cmd + strlen(LOGGRAPH" ") * sizeof(char);
3994                                 } else if(strncasecmp(obj->data.s, NORMGRAPH" ", strlen(NORMGRAPH" ")) == EQUAL) {
3995                                         cmd = cmd + strlen(NORMGRAPH" ") * sizeof(char);
3996                                 }
3997                                 if (strstr(cmd, " "TEMPGRAD) && strlen(cmd) > strlen(" "TEMPGRAD)) {
3998                                         tempgrad = TRUE;
3999                                         cmd += strlen(" "TEMPGRAD);
4000                                 }
4001                                 read_exec(cmd, p, text_buffer_size);
4002                                 barnum = get_barnum(p);
4003
4004                                 if (barnum >= 0.0) {
4005                                         new_graph(p, obj->a, obj->b, obj->c, obj->d, round_to_int(barnum),
4006                                                         100, 1, showaslog, tempgrad);
4007                                 }
4008                         }
4009 #endif /* X11 */
4010                         OBJ(execibar) {
4011                                 if (current_update_time - obj->data.execi.last_update
4012                                                 >= obj->data.execi.interval) {
4013                                         double barnum;
4014
4015                                         read_exec(obj->data.execi.cmd, p, text_buffer_size);
4016                                         barnum = get_barnum(p);
4017
4018                                         if (barnum >= 0.0) {
4019                                                 obj->f = barnum;
4020                                         }
4021                                         obj->data.execi.last_update = current_update_time;
4022                                 }
4023 #ifdef X11
4024                                 if(output_methods & TO_X) {
4025                                         new_bar(p, obj->a, obj->b, round_to_int(obj->f * 2.55));
4026                                 } else {
4027 #endif /* X11 */
4028                                         if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
4029                                         new_bar_in_shell(p, p_max_size, round_to_int(obj->f), obj->a);
4030 #ifdef X11
4031                                 }
4032 #endif /* X11 */
4033                         }
4034 #ifdef X11
4035                         OBJ(execigraph) {
4036                                 if (current_update_time - obj->data.execi.last_update
4037                                                 >= obj->data.execi.interval) {
4038                                         double barnum;
4039                                         char showaslog = FALSE;
4040                                         char tempgrad = FALSE;
4041                                         char *cmd = obj->data.execi.cmd;
4042
4043                                         if (strncasecmp(obj->data.execi.cmd, LOGGRAPH" ", strlen(LOGGRAPH" ")) == EQUAL) {
4044                                                 showaslog = TRUE;
4045                                                 cmd = cmd + strlen(LOGGRAPH" ") * sizeof(char);
4046                                         } else if(strncasecmp(obj->data.s, NORMGRAPH" ", strlen(NORMGRAPH" ")) == EQUAL) {
4047                                                 cmd = cmd + strlen(NORMGRAPH" ") * sizeof(char);
4048                                         }
4049                                         if (strstr(cmd, " "TEMPGRAD) && strlen(cmd) > strlen(" "TEMPGRAD)) {
4050                                                 tempgrad = TRUE;
4051                                                 cmd += strlen(" "TEMPGRAD);
4052                                         }
4053                                         obj->char_a = showaslog;
4054                                         obj->char_b = tempgrad;
4055                                         read_exec(cmd, p, text_buffer_size);
4056                                         barnum = get_barnum(p);
4057
4058                                         if (barnum >= 0.0) {
4059                                                 obj->f = barnum;
4060                                         }
4061                                         obj->data.execi.last_update = current_update_time;
4062                                 }
4063                                 new_graph(p, obj->a, obj->b, obj->c, obj->d, (int) (obj->f), 100, 1, obj->char_a, obj->char_b);
4064                         }
4065                         OBJ(execigauge) {
4066                                 if (current_update_time - obj->data.execi.last_update
4067                                                 >= obj->data.execi.interval) {
4068                                         double barnum;
4069
4070                                         read_exec(obj->data.execi.cmd, p, text_buffer_size);
4071                                         barnum = get_barnum(p);
4072
4073                                         if (barnum >= 0.0) {
4074                                                 obj->f = 255 * barnum / 100.0;
4075                                         }
4076                                         obj->data.execi.last_update = current_update_time;
4077                                 }
4078                                 new_gauge(p, obj->a, obj->b, round_to_int(obj->f));
4079                         }
4080 #endif /* X11 */
4081                         OBJ(execi) {
4082                                 if (current_update_time - obj->data.execi.last_update
4083                                                 >= obj->data.execi.interval
4084                                                 && obj->data.execi.interval != 0) {
4085                                         read_exec(obj->data.execi.cmd, obj->data.execi.buffer,
4086                                                 text_buffer_size);
4087                                         obj->data.execi.last_update = current_update_time;
4088                                 }
4089                                 snprintf(p, text_buffer_size, "%s", obj->data.execi.buffer);
4090                         }
4091                         OBJ(execpi) {
4092                                 struct text_object subroot;
4093                                 struct information *tmp_info =
4094                                         malloc(sizeof(struct information));
4095                                 memcpy(tmp_info, cur, sizeof(struct information));
4096
4097                                 if (current_update_time - obj->data.execi.last_update
4098                                                 < obj->data.execi.interval
4099                                                 || obj->data.execi.interval == 0) {
4100                                         parse_conky_vars(&subroot, obj->data.execi.buffer, p, tmp_info);
4101                                 } else {
4102                                         char *output = obj->data.execi.buffer;
4103                                         FILE *fp = popen(obj->data.execi.cmd, "r");
4104                                         int length = fread(output, 1, text_buffer_size, fp);
4105
4106                                         pclose(fp);
4107
4108                                         output[length] = '\0';
4109                                         if (length > 0 && output[length - 1] == '\n') {
4110                                                 output[length - 1] = '\0';
4111                                         }
4112
4113                                         parse_conky_vars(&subroot, obj->data.execi.buffer, p, tmp_info);
4114                                         obj->data.execi.last_update = current_update_time;
4115                                 }
4116                                 free_text_objects(&subroot, 1);
4117                                 free(tmp_info);
4118                         }
4119                         OBJ(texeci) {
4120                                 if (!obj->data.texeci.p_timed_thread) {
4121                                         obj->data.texeci.p_timed_thread =
4122                                                 timed_thread_create(&threaded_exec,
4123                                                 (void *) obj, obj->data.texeci.interval * 1000000);
4124                                         if (!obj->data.texeci.p_timed_thread) {
4125                                                 ERR("Error creating texeci timed thread");
4126                                         }
4127                                         timed_thread_register(obj->data.texeci.p_timed_thread,
4128                                                 &obj->data.texeci.p_timed_thread);
4129                                         if (timed_thread_run(obj->data.texeci.p_timed_thread)) {
4130                                                 ERR("Error running texeci timed thread");
4131                                         }
4132                                 } else {
4133                                         timed_thread_lock(obj->data.texeci.p_timed_thread);
4134                                         snprintf(p, text_buffer_size, "%s", obj->data.texeci.buffer);
4135                                         timed_thread_unlock(obj->data.texeci.p_timed_thread);
4136                                 }
4137                         }
4138 #endif /* HAVE_POPEN */
4139                         OBJ(imap_unseen) {
4140                                 struct mail_s *mail = ensure_mail_thread(obj, imap_thread, "imap");
4141
4142                                 if (mail && mail->p_timed_thread) {
4143                                         timed_thread_lock(mail->p_timed_thread);
4144                                         snprintf(p, p_max_size, "%lu", mail->unseen);
4145                                         timed_thread_unlock(mail->p_timed_thread);
4146                                 }
4147                         }
4148                         OBJ(imap_messages) {
4149                                 struct mail_s *mail = ensure_mail_thread(obj, imap_thread, "imap");
4150
4151                                 if (mail && mail->p_timed_thread) {
4152                                         timed_thread_lock(mail->p_timed_thread);
4153                                         snprintf(p, p_max_size, "%lu", mail->messages);
4154                                         timed_thread_unlock(mail->p_timed_thread);
4155                                 }
4156                         }
4157                         OBJ(pop3_unseen) {
4158                                 struct mail_s *mail = ensure_mail_thread(obj, pop3_thread, "pop3");
4159
4160                                 if (mail && mail->p_timed_thread) {
4161                                         timed_thread_lock(mail->p_timed_thread);
4162                                         snprintf(p, p_max_size, "%lu", mail->unseen);
4163                                         timed_thread_unlock(mail->p_timed_thread);
4164                                 }
4165                         }
4166                         OBJ(pop3_used) {
4167                                 struct mail_s *mail = ensure_mail_thread(obj, pop3_thread, "pop3");
4168
4169                                 if (mail && mail->p_timed_thread) {
4170                                         timed_thread_lock(mail->p_timed_thread);
4171                                         snprintf(p, p_max_size, "%.1f",
4172                                                 mail->used / 1024.0 / 1024.0);
4173                                         timed_thread_unlock(mail->p_timed_thread);
4174                                 }
4175                         }
4176                         OBJ(fs_bar) {
4177                                 if (obj->data.fs != NULL) {
4178                                         if (obj->data.fs->size == 0) {
4179 #ifdef X11
4180                                                 if(output_methods & TO_X) {
4181                                                         new_bar(p, obj->data.fsbar.w, obj->data.fsbar.h, 255);
4182                                                 }else{
4183 #endif /* X11 */
4184                                                         if(!obj->data.fsbar.w) obj->data.fsbar.w = DEFAULT_BAR_WIDTH_NO_X;
4185                                                         new_bar_in_shell(p, p_max_size, 100, obj->data.fsbar.w);
4186 #ifdef X11
4187                                                 }
4188 #endif /* X11 */
4189                                         } else {
4190 #ifdef X11
4191                                                 if(output_methods & TO_X) {
4192                                                         new_bar(p, obj->data.fsbar.w, obj->data.fsbar.h,
4193                                                                 (int) (255 - obj->data.fsbar.fs->avail * 255 /
4194                                                                 obj->data.fs->size));
4195                                                 }else{
4196 #endif /* X11 */
4197                                                         if(!obj->data.fsbar.w) obj->data.fsbar.w = DEFAULT_BAR_WIDTH_NO_X;
4198                                                         new_bar_in_shell(p, p_max_size,
4199                                                                 (int) (100 - obj->data.fsbar.fs->avail * 100 / obj->data.fs->size), obj->data.fsbar.w);
4200 #ifdef X11
4201                                                 }
4202 #endif /* X11 */
4203                                         }
4204                                 }
4205                         }
4206                         OBJ(fs_free) {
4207                                 if (obj->data.fs != NULL) {
4208                                         human_readable( (obj->data.fs->free ? obj->data.fs->free :
4209                                                                 obj->data.fs->avail), p, 255);
4210                                 }
4211                         }
4212                         OBJ(fs_free_perc) {
4213                                 if (obj->data.fs != NULL) {
4214                                         int val = 0;
4215
4216                                         if (obj->data.fs->size) {
4217                                                 val = (obj->data.fs->free ? obj->data.fs->free :
4218                                                                 obj->data.fs->avail) * 100 /
4219                                                         obj->data.fs->size;
4220                                         }
4221
4222                                         percent_print(p, p_max_size, val);
4223                                 }
4224                         }
4225                         OBJ(fs_size) {
4226                                 if (obj->data.fs != NULL) {
4227                                         human_readable(obj->data.fs->size, p, 255);
4228                                 }
4229                         }
4230                         OBJ(fs_type) {
4231                                 if (obj->data.fs != NULL)
4232                                         snprintf(p, p_max_size, "%s", obj->data.fs->type);
4233                         }
4234                         OBJ(fs_used) {
4235                                 if (obj->data.fs != NULL) {
4236                                         human_readable(obj->data.fs->size - (obj->data.fs->free
4237                                                 ? obj->data.fs->free : obj->data.fs->avail), p, 255);
4238                                 }
4239                         }
4240                         OBJ(fs_bar_free) {
4241                                 if (obj->data.fs != NULL) {
4242                                         if (obj->data.fs->size == 0) {
4243 #ifdef X11
4244                                                 if(output_methods & TO_X) {
4245                                                         new_bar(p, obj->data.fsbar.w, obj->data.fsbar.h, 255);
4246                                                 }else{
4247 #endif /* X11 */
4248                                                         if(!obj->data.fsbar.w) obj->data.fsbar.w = DEFAULT_BAR_WIDTH_NO_X;
4249                                                         new_bar_in_shell(p, p_max_size, 100, obj->data.fsbar.w);
4250 #ifdef X11
4251                                                 }
4252 #endif /* X11 */
4253                                         } else {
4254 #ifdef X11
4255                                                 if(output_methods & TO_X) {
4256                                                         new_bar(p, obj->data.fsbar.w, obj->data.fsbar.h,
4257                                                                 (int) (obj->data.fsbar.fs->avail * 255 /
4258                                                                 obj->data.fs->size));
4259                                                 }else{
4260 #endif /* X11 */
4261                                                         if(!obj->data.fsbar.w) obj->data.fsbar.w = DEFAULT_BAR_WIDTH_NO_X;
4262                                                         new_bar_in_shell(p, p_max_size,
4263                                                                 (int) (obj->data.fsbar.fs->avail * 100 / obj->data.fs->size), obj->data.fsbar.w);
4264 #ifdef X11
4265                                                 }
4266 #endif /* X11 */
4267                                         }
4268                                 }
4269                         }
4270                         OBJ(fs_used_perc) {
4271                                 if (obj->data.fs != NULL) {
4272                                         int val = 0;
4273
4274                                         if (obj->data.fs->size) {
4275                                                 val = (obj->data.fs->free ? obj->data.fs->free :
4276                                                                 obj->data.fs->avail) * 100 /
4277                                                         obj->data.fs->size;
4278                                         }
4279
4280                                         percent_print(p, p_max_size, 100 - val);
4281                                 }
4282                         }
4283                         OBJ(loadavg) {
4284                                 float *v = info.loadavg;
4285
4286                                 if (obj->data.loadavg[2]) {
4287                                         snprintf(p, p_max_size, "%.2f %.2f %.2f",
4288                                                 v[obj->data.loadavg[0] - 1],
4289                                                 v[obj->data.loadavg[1] - 1],
4290                                                 v[obj->data.loadavg[2] - 1]);
4291                                 } else if (obj->data.loadavg[1]) {
4292                                         snprintf(p, p_max_size, "%.2f %.2f",
4293                                                 v[obj->data.loadavg[0] - 1],
4294                                                 v[obj->data.loadavg[1] - 1]);
4295                                 } else if (obj->data.loadavg[0]) {
4296                                         snprintf(p, p_max_size, "%.2f",
4297                                                 v[obj->data.loadavg[0] - 1]);
4298                                 }
4299                         }
4300                         OBJ(goto) {
4301                                 new_goto(p, obj->data.i);
4302                         }
4303                         OBJ(tab) {
4304                                 new_tab(p, obj->data.pair.a, obj->data.pair.b);
4305                         }
4306 #ifdef X11
4307                         OBJ(hr) {
4308                                 new_hr(p, obj->data.i);
4309                         }
4310 #endif
4311                         OBJ(nameserver) {
4312                                 if (cur->nameserver_info.nscount > obj->data.i)
4313                                         snprintf(p, p_max_size, "%s",
4314                                                         cur->nameserver_info.ns_list[obj->data.i]);
4315                         }
4316 #ifdef EVE
4317                         OBJ(eve) {
4318                                 char *skill = eve(obj->data.eve.userid, obj->data.eve.apikey, obj->data.eve.charid);
4319                                 snprintf(p, p_max_size, "%s", skill);
4320                         }
4321 #endif
4322 #ifdef RSS
4323                         OBJ(rss) {
4324                                 PRSS *data = get_rss_info(obj->data.rss.uri,
4325                                         obj->data.rss.delay);
4326                                 char *str;
4327
4328                                 if (data == NULL) {
4329                                         snprintf(p, p_max_size, "prss: Error reading RSS data\n");
4330                                 } else {
4331                                         if (strcmp(obj->data.rss.action, "feed_title") == EQUAL) {
4332                                                 str = data->title;
4333                                                 // remove trailing new line if one exists
4334                                                 if (str[strlen(str) - 1] == '\n') {
4335                                                         str[strlen(str) - 1] = 0;
4336                                                 }
4337                                                 snprintf(p, p_max_size, "%s", str);
4338                                         } else if (strcmp(obj->data.rss.action, "item_title") == EQUAL) {
4339                                                 if (obj->data.rss.act_par < data->item_count) {
4340                                                         str = data->items[obj->data.rss.act_par].title;
4341                                                         // remove trailing new line if one exists
4342                                                         if (str[strlen(str) - 1] == '\n') {
4343                                                                 str[strlen(str) - 1] = 0;
4344                                                         }
4345                                                         snprintf(p, p_max_size, "%s", str);
4346                                                 }
4347                                         } else if (strcmp(obj->data.rss.action, "item_desc") == EQUAL) {
4348                                                 if (obj->data.rss.act_par < data->item_count) {
4349                                                         str =
4350                                                                 data->items[obj->data.rss.act_par].description;
4351                                                         // remove trailing new line if one exists
4352                                                         if (str[strlen(str) - 1] == '\n') {
4353                                                                 str[strlen(str) - 1] = 0;
4354                                                         }
4355                                                         snprintf(p, p_max_size, "%s", str);
4356                                                 }
4357                                         } else if (strcmp(obj->data.rss.action, "item_titles") == EQUAL) {
4358                                                 if (data->item_count > 0) {
4359                                                         int itmp;
4360                                                         int show;
4361                                                         //'tmpspaces' is a string with spaces too be placed in front of each title
4362                                                         char *tmpspaces = malloc(obj->data.rss.nrspaces + 1);
4363                                                         memset(tmpspaces, ' ', obj->data.rss.nrspaces);
4364                                                         tmpspaces[obj->data.rss.nrspaces]=0;
4365
4366                                                         p[0] = 0;
4367
4368                                                         if (obj->data.rss.act_par > data->item_count) {
4369                                                                 show = data->item_count;
4370                                                         } else {
4371                                                                 show = obj->data.rss.act_par;
4372                                                         }
4373                                                         for (itmp = 0; itmp < show; itmp++) {
4374                                                                 PRSS_Item *item = &data->items[itmp];
4375
4376                                                                 str = item->title;
4377                                                                 if (str) {
4378                                                                         // don't add new line before first item
4379                                                                         if (itmp > 0) {
4380                                                                                 strncat(p, "\n", p_max_size);
4381                                                                         }
4382                                                                         /* remove trailing new line if one exists,
4383                                                                          * we have our own */
4384                                                                         if (str[strlen(str) - 1] == '\n') {
4385                                                                                 str[strlen(str) - 1] = 0;
4386                                                                         }
4387                                                                         strncat(p, tmpspaces, p_max_size);
4388                                                                         strncat(p, str, p_max_size);
4389                                                                 }
4390                                                         }
4391                                                         free(tmpspaces);
4392                                                 }
4393                                         }
4394                                 }
4395                         }
4396 #endif
4397 #ifdef HAVE_LUA
4398                         OBJ(lua) {
4399                                 char *str = llua_getstring(obj->data.s);
4400                                 if (str) {
4401                                         snprintf(p, p_max_size, "%s", str);
4402                                         free(str);
4403                                 }
4404                         }
4405                         OBJ(lua_parse) {
4406                                 char *str = llua_getstring(obj->data.s);
4407                                 if (str) {
4408                                         struct information *tmp_info;
4409                                         struct text_object subroot;
4410
4411                                         tmp_info = malloc(sizeof(struct information));
4412                                         memcpy(tmp_info, cur, sizeof(struct information));
4413                                         parse_conky_vars(&subroot, str, p, tmp_info);
4414
4415                                         free_text_objects(&subroot, 1);
4416                                         free(tmp_info);
4417                                         free(str);
4418                                 }
4419                         }
4420                         OBJ(lua_read_parse) {
4421                                 struct information *tmp_info;
4422                                 struct text_object subroot, subroot2;
4423                                 char func[64];
4424                                 char *text, *str;
4425                                 sscanf(obj->data.s, "%64s", func);
4426                                 text = obj->data.s + strlen(func) + 1;
4427
4428                                 tmp_info = malloc(sizeof(struct information));
4429                                 memcpy(tmp_info, cur, sizeof(struct information));
4430                                 parse_conky_vars(&subroot, text, p, tmp_info);
4431                                 DBGP("evaluated '%s' to '%s'", text, p);
4432
4433                                 str = llua_getstring_read(func, p);
4434                                 if (str) {
4435                                         parse_conky_vars(&subroot2, str, p, tmp_info);
4436                                         DBGP("evaluated '%s' to '%s'", str, p);
4437
4438                                         free(str);
4439                                         free_text_objects(&subroot2, 1);
4440                                 }
4441                                 free_text_objects(&subroot, 1);
4442                                 free(tmp_info);
4443                         }
4444                         OBJ(lua_bar) {
4445                                 int per;
4446                                 if (llua_getinteger(obj->data.s, &per)) {
4447 #ifdef X11
4448                                         if(output_methods & TO_X) {
4449                                                 new_bar(p, obj->a, obj->b, (per/100.0 * 255));
4450                                         } else {
4451 #endif /* X11 */
4452                                                 if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
4453                                                 new_bar_in_shell(p, p_max_size, per, obj->a);
4454 #ifdef X11
4455                                         }
4456 #endif /* X11 */
4457                                 }
4458                         }
4459 #ifdef X11
4460                         OBJ(lua_graph) {
4461                                 int per;
4462                                 if (llua_getinteger(obj->data.s, &per)) {
4463                                         new_graph(p, obj->a, obj->b, obj->c, obj->d,
4464                                                         (per/100.0 * 255), 100, 1, obj->char_a, obj->char_b);
4465                                 }
4466                         }
4467                         OBJ(lua_gauge) {
4468                                 int per;
4469                                 if (llua_getinteger(obj->data.s, &per)) {
4470                                         new_gauge(p, obj->a, obj->b, (per/100.0 * 255));
4471                                 }
4472                         }
4473 #endif /* X11 */
4474 #endif /* HAVE_LUA */
4475 #ifdef HDDTEMP
4476                         OBJ(hddtemp) {
4477                                 char *endptr, unit;
4478                                 long val;
4479                                 if (obj->data.hddtemp.update_time < current_update_time - 30) {
4480                                         if (obj->data.hddtemp.temp)
4481                                                 free(obj->data.hddtemp.temp);
4482                                         obj->data.hddtemp.temp = get_hddtemp_info(obj->data.hddtemp.dev,
4483                                                         obj->data.hddtemp.addr, obj->data.hddtemp.port);
4484                                         obj->data.hddtemp.update_time = current_update_time;
4485                                 }
4486                                 if (!obj->data.hddtemp.temp) {
4487                                         snprintf(p, p_max_size, "N/A");
4488                                 } else {
4489                                         val = strtol(obj->data.hddtemp.temp + 1, &endptr, 10);
4490                                         unit = obj->data.hddtemp.temp[0];
4491
4492                                         if (*endptr != '\0')
4493                                                 snprintf(p, p_max_size, "N/A");
4494                                         else if (unit == 'C')
4495                                                 temp_print(p, p_max_size, (double)val, TEMP_CELSIUS);
4496                                         else if (unit == 'F')
4497                                                 temp_print(p, p_max_size, (double)val, TEMP_FAHRENHEIT);
4498                                         else
4499                                                 snprintf(p, p_max_size, "N/A");
4500                                 }
4501                         }
4502 #endif
4503                         OBJ(offset) {
4504                                 new_offset(p, obj->data.i);
4505                         }
4506                         OBJ(voffset) {
4507                                 new_voffset(p, obj->data.i);
4508                         }
4509 #ifdef __linux__
4510                         OBJ(i2c) {
4511                                 double r;
4512
4513                                 r = get_sysfs_info(&obj->data.sysfs.fd, obj->data.sysfs.arg,
4514                                         obj->data.sysfs.devtype, obj->data.sysfs.type);
4515
4516                                 r = r * obj->data.sysfs.factor + obj->data.sysfs.offset;
4517
4518                                 if (!strncmp(obj->data.sysfs.type, "temp", 4)) {
4519                                         temp_print(p, p_max_size, r, TEMP_CELSIUS);
4520                                 } else if (r >= 100.0 || r == 0) {
4521                                         snprintf(p, p_max_size, "%d", (int) r);
4522                                 } else {
4523                                         snprintf(p, p_max_size, "%.1f", r);
4524                                 }
4525                         }
4526                         OBJ(platform) {
4527                                 double r;
4528
4529                                 r = get_sysfs_info(&obj->data.sysfs.fd, obj->data.sysfs.arg,
4530                                         obj->data.sysfs.devtype, obj->data.sysfs.type);
4531
4532                                 r = r * obj->data.sysfs.factor + obj->data.sysfs.offset;
4533
4534                                 if (!strncmp(obj->data.sysfs.type, "temp", 4)) {
4535                                         temp_print(p, p_max_size, r, TEMP_CELSIUS);
4536                                 } else if (r >= 100.0 || r == 0) {
4537                                         snprintf(p, p_max_size, "%d", (int) r);
4538                                 } else {
4539                                         snprintf(p, p_max_size, "%.1f", r);
4540                                 }
4541                         }
4542                         OBJ(hwmon) {
4543                                 double r;
4544
4545                                 r = get_sysfs_info(&obj->data.sysfs.fd, obj->data.sysfs.arg,
4546                                         obj->data.sysfs.devtype, obj->data.sysfs.type);
4547
4548                                 r = r * obj->data.sysfs.factor + obj->data.sysfs.offset;
4549
4550                                 if (!strncmp(obj->data.sysfs.type, "temp", 4)) {
4551                                         temp_print(p, p_max_size, r, TEMP_CELSIUS);
4552                                 } else if (r >= 100.0 || r == 0) {
4553                                         snprintf(p, p_max_size, "%d", (int) r);
4554                                 } else {
4555                                         snprintf(p, p_max_size, "%.1f", r);
4556                                 }
4557                         }
4558 #endif /* __linux__ */
4559                         OBJ(alignr) {
4560                                 new_alignr(p, obj->data.i);
4561                         }
4562                         OBJ(alignc) {
4563                                 new_alignc(p, obj->data.i);
4564                         }
4565                         OBJ(if_empty) {
4566                                 char buf[max_user_text];
4567                                 struct information *tmp_info =
4568                                         malloc(sizeof(struct information));
4569                                 memcpy(tmp_info, cur, sizeof(struct information));
4570                                 generate_text_internal(buf, max_user_text,
4571                                                        *obj->sub, tmp_info);
4572
4573                                 if (strlen(buf) != 0) {
4574                                         DO_JUMP;
4575                                 }
4576                                 free(tmp_info);
4577                         }
4578                         OBJ(if_match) {
4579                                 char expression[max_user_text];
4580                                 int val;
4581                                 struct information *tmp_info;
4582
4583                                 tmp_info = malloc(sizeof(struct information));
4584                                 memcpy(tmp_info, cur, sizeof(struct information));
4585                                 generate_text_internal(expression, max_user_text,
4586                                                        *obj->sub, tmp_info);
4587                                 DBGP("parsed arg into '%s'", expression);
4588
4589                                 val = compare(expression);
4590                                 if (val == -2) {
4591                                         ERR("compare failed for expression '%s'",
4592                                                         expression);
4593                                 } else if (!val) {
4594                                         DO_JUMP;
4595                                 }
4596                                 free(tmp_info);
4597                         }
4598                         OBJ(if_existing) {
4599                                 if (obj->data.ifblock.str
4600                                     && !check_contains(obj->data.ifblock.s,
4601                                                        obj->data.ifblock.str)) {
4602                                         DO_JUMP;
4603                                 } else if (obj->data.ifblock.s
4604                                            && access(obj->data.ifblock.s, F_OK)) {
4605                                         DO_JUMP;
4606                                 }
4607                         }
4608                         OBJ(if_mounted) {
4609                                 if ((obj->data.ifblock.s)
4610                                                 && (!check_mount(obj->data.ifblock.s))) {
4611                                         DO_JUMP;
4612                                 }
4613                         }
4614                         OBJ(if_running) {
4615                                 if ((obj->data.ifblock.s) && system(obj->data.ifblock.s)) {
4616                                         DO_JUMP;
4617                                 }
4618                         }
4619 #if defined(__linux__)
4620                         OBJ(ioscheduler) {
4621                                 snprintf(p, p_max_size, "%s", get_ioscheduler(obj->data.s));
4622                         }
4623 #endif
4624                         OBJ(kernel) {
4625                                 snprintf(p, p_max_size, "%s", cur->uname_s.release);
4626                         }
4627                         OBJ(machine) {
4628                                 snprintf(p, p_max_size, "%s", cur->uname_s.machine);
4629                         }
4630
4631                         /* memory stuff */
4632                         OBJ(mem) {
4633                                 human_readable(cur->mem * 1024, p, 255);
4634                         }
4635                         OBJ(memeasyfree) {
4636                                 human_readable(cur->memeasyfree * 1024, p, 255);
4637                         }
4638                         OBJ(memfree) {
4639                                 human_readable(cur->memfree * 1024, p, 255);
4640                         }
4641                         OBJ(memmax) {
4642                                 human_readable(cur->memmax * 1024, p, 255);
4643                         }
4644                         OBJ(memperc) {
4645                                 if (cur->memmax)
4646                                         percent_print(p, p_max_size, cur->mem * 100 / cur->memmax);
4647                         }
4648 #ifdef X11
4649                         OBJ(memgauge){
4650                                 new_gauge(p, obj->data.pair.a, obj->data.pair.b,
4651                                         cur->memmax ? (cur->mem * 255) / (cur->memmax) : 0);
4652                         }
4653 #endif /* X11 */
4654                         OBJ(membar) {
4655 #ifdef X11
4656                                 if(output_methods & TO_X) {
4657                                         new_bar(p, obj->data.pair.a, obj->data.pair.b,
4658                                                 cur->memmax ? (cur->mem * 255) / (cur->memmax) : 0);
4659                                 }else{
4660 #endif /* X11 */
4661                                         if(!obj->data.pair.a) obj->data.pair.a = DEFAULT_BAR_WIDTH_NO_X;
4662                                         new_bar_in_shell(p, p_max_size, cur->memmax ? (cur->mem * 100) / (cur->memmax) : 0, obj->data.pair.a);
4663 #ifdef X11
4664                                 }
4665 #endif /* X11 */
4666                         }
4667 #ifdef X11
4668                         OBJ(memgraph) {
4669                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
4670                                         cur->memmax ? (cur->mem * 100.0) / (cur->memmax) : 0.0,
4671                                         100, 1, obj->char_a, obj->char_b);
4672                         }
4673 #endif /* X11 */
4674                         /* mixer stuff */
4675                         OBJ(mixer) {
4676                                 percent_print(p, p_max_size, mixer_get_avg(obj->data.l));
4677                         }
4678                         OBJ(mixerl) {
4679                                 percent_print(p, p_max_size, mixer_get_left(obj->data.l));
4680                         }
4681                         OBJ(mixerr) {
4682                                 percent_print(p, p_max_size, mixer_get_right(obj->data.l));
4683                         }
4684 #ifdef X11
4685                         OBJ(mixerbar) {
4686                                 new_bar(p, obj->data.mixerbar.w, obj->data.mixerbar.h,
4687                                         mixer_to_255(obj->data.mixerbar.l,mixer_get_avg(obj->data.mixerbar.l)));
4688                         }
4689                         OBJ(mixerlbar) {
4690                                 new_bar(p, obj->data.mixerbar.w, obj->data.mixerbar.h,
4691                                         mixer_to_255(obj->data.mixerbar.l,mixer_get_left(obj->data.mixerbar.l)));
4692                         }
4693                         OBJ(mixerrbar) {
4694                                 new_bar(p, obj->data.mixerbar.w, obj->data.mixerbar.h,
4695                                         mixer_to_255(obj->data.mixerbar.l,mixer_get_right(obj->data.mixerbar.l)));
4696                         }
4697 #endif /* X11 */
4698                         OBJ(if_mixer_mute) {
4699                                 if (!mixer_is_mute(obj->data.ifblock.i)) {
4700                                         DO_JUMP;
4701                                 }
4702                         }
4703 #ifdef X11
4704                         OBJ(monitor) {
4705                                 snprintf(p, p_max_size, "%d", cur->x11.monitor.current);
4706                         }
4707                         OBJ(monitor_number) {
4708                                 snprintf(p, p_max_size, "%d", cur->x11.monitor.number);
4709                         }
4710 #endif /* X11 */
4711
4712                         /* mail stuff */
4713                         OBJ(mails) {
4714                                 update_mail_count(&obj->data.local_mail);
4715                                 snprintf(p, p_max_size, "%d", obj->data.local_mail.mail_count);
4716                         }
4717                         OBJ(new_mails) {
4718                                 update_mail_count(&obj->data.local_mail);
4719                                 snprintf(p, p_max_size, "%d",
4720                                         obj->data.local_mail.new_mail_count);
4721                         }
4722                         OBJ(seen_mails) {
4723                                 update_mail_count(&obj->data.local_mail);
4724                                 snprintf(p, p_max_size, "%d",
4725                                         obj->data.local_mail.seen_mail_count);
4726                         }
4727                         OBJ(unseen_mails) {
4728                                 update_mail_count(&obj->data.local_mail);
4729                                 snprintf(p, p_max_size, "%d",
4730                                         obj->data.local_mail.unseen_mail_count);
4731                         }
4732                         OBJ(flagged_mails) {
4733                                 update_mail_count(&obj->data.local_mail);
4734                                 snprintf(p, p_max_size, "%d",
4735                                         obj->data.local_mail.flagged_mail_count);
4736                         }
4737                         OBJ(unflagged_mails) {
4738                                 update_mail_count(&obj->data.local_mail);
4739                                 snprintf(p, p_max_size, "%d",
4740                                         obj->data.local_mail.unflagged_mail_count);
4741                         }
4742                         OBJ(forwarded_mails) {
4743                                 update_mail_count(&obj->data.local_mail);
4744                                 snprintf(p, p_max_size, "%d",
4745                                         obj->data.local_mail.forwarded_mail_count);
4746                         }
4747                         OBJ(unforwarded_mails) {
4748                                 update_mail_count(&obj->data.local_mail);
4749                                 snprintf(p, p_max_size, "%d",
4750                                         obj->data.local_mail.unforwarded_mail_count);
4751                         }
4752                         OBJ(replied_mails) {
4753                                 update_mail_count(&obj->data.local_mail);
4754                                 snprintf(p, p_max_size, "%d",
4755                                         obj->data.local_mail.replied_mail_count);
4756                         }
4757                         OBJ(unreplied_mails) {
4758                                 update_mail_count(&obj->data.local_mail);
4759                                 snprintf(p, p_max_size, "%d",
4760                                         obj->data.local_mail.unreplied_mail_count);
4761                         }
4762                         OBJ(draft_mails) {
4763                                 update_mail_count(&obj->data.local_mail);
4764                                 snprintf(p, p_max_size, "%d",
4765                                         obj->data.local_mail.draft_mail_count);
4766                         }
4767                         OBJ(trashed_mails) {
4768                                 update_mail_count(&obj->data.local_mail);
4769                                 snprintf(p, p_max_size, "%d",
4770                                         obj->data.local_mail.trashed_mail_count);
4771                         }
4772                         OBJ(mboxscan) {
4773                                 mbox_scan(obj->data.mboxscan.args, obj->data.mboxscan.output,
4774                                         text_buffer_size);
4775                                 snprintf(p, p_max_size, "%s", obj->data.mboxscan.output);
4776                         }
4777                         OBJ(nodename) {
4778                                 snprintf(p, p_max_size, "%s", cur->uname_s.nodename);
4779                         }
4780                         OBJ(outlinecolor) {
4781                                 new_outline(p, obj->data.l);
4782                         }
4783                         OBJ(processes) {
4784                                 spaced_print(p, p_max_size, "%hu", 4, cur->procs);
4785                         }
4786                         OBJ(running_processes) {
4787                                 spaced_print(p, p_max_size, "%hu", 4, cur->run_procs);
4788                         }
4789                         OBJ(text) {
4790                                 snprintf(p, p_max_size, "%s", obj->data.s);
4791                         }
4792 #ifdef X11
4793                         OBJ(shadecolor) {
4794                                 new_bg(p, obj->data.l);
4795                         }
4796                         OBJ(stippled_hr) {
4797                                 new_stippled_hr(p, obj->data.pair.a, obj->data.pair.b);
4798                         }
4799 #endif /* X11 */
4800                         OBJ(swap) {
4801                                 human_readable(cur->swap * 1024, p, 255);
4802                         }
4803                         OBJ(swapmax) {
4804                                 human_readable(cur->swapmax * 1024, p, 255);
4805                         }
4806                         OBJ(swapperc) {
4807                                 if (cur->swapmax == 0) {
4808                                         strncpy(p, "No swap", p_max_size);
4809                                 } else {
4810                                         percent_print(p, p_max_size, cur->swap * 100 / cur->swapmax);
4811                                 }
4812                         }
4813                         OBJ(swapbar) {
4814 #ifdef X11
4815                                 if(output_methods & TO_X) {
4816                                         new_bar(p, obj->data.pair.a, obj->data.pair.b,
4817                                                 cur->swapmax ? (cur->swap * 255) / (cur->swapmax) : 0);
4818                                 }else{
4819 #endif /* X11 */
4820                                         if(!obj->data.pair.a) obj->data.pair.a = DEFAULT_BAR_WIDTH_NO_X;
4821                                         new_bar_in_shell(p, p_max_size, cur->swapmax ? (cur->swap * 100) / (cur->swapmax) : 0, obj->data.pair.a);
4822 #ifdef X11
4823                                 }
4824 #endif /* X11 */
4825                         }
4826                         OBJ(sysname) {
4827                                 snprintf(p, p_max_size, "%s", cur->uname_s.sysname);
4828                         }
4829                         OBJ(time) {
4830                                 time_t t = time(NULL);
4831                                 struct tm *tm = localtime(&t);
4832
4833                                 setlocale(LC_TIME, "");
4834                                 strftime(p, p_max_size, obj->data.s, tm);
4835                         }
4836                         OBJ(utime) {
4837                                 time_t t = time(NULL);
4838                                 struct tm *tm = gmtime(&t);
4839
4840                                 strftime(p, p_max_size, obj->data.s, tm);
4841                         }
4842                         OBJ(tztime) {
4843                                 char *oldTZ = NULL;
4844                                 time_t t;
4845                                 struct tm *tm;
4846
4847                                 if (obj->data.tztime.tz) {
4848                                         oldTZ = getenv("TZ");
4849                                         setenv("TZ", obj->data.tztime.tz, 1);
4850                                         tzset();
4851                                 }
4852                                 t = time(NULL);
4853                                 tm = localtime(&t);
4854
4855                                 setlocale(LC_TIME, "");
4856                                 strftime(p, p_max_size, obj->data.tztime.fmt, tm);
4857                                 if (oldTZ) {
4858                                         setenv("TZ", oldTZ, 1);
4859                                         tzset();
4860                                 } else {
4861                                         unsetenv("TZ");
4862                                 }
4863                                 // Needless to free oldTZ since getenv gives ptr to static data
4864                         }
4865                         OBJ(totaldown) {
4866                                 human_readable(obj->data.net->recv, p, 255);
4867                         }
4868                         OBJ(totalup) {
4869                                 human_readable(obj->data.net->trans, p, 255);
4870                         }
4871                         OBJ(updates) {
4872                                 snprintf(p, p_max_size, "%d", total_updates);
4873                         }
4874                         OBJ(if_updatenr) {
4875                                 if(total_updates % updatereset != obj->data.ifblock.i - 1) {
4876                                         DO_JUMP;
4877                                 }
4878                         }
4879                         OBJ(upspeed) {
4880                                 human_readable(obj->data.net->trans_speed, p, 255);
4881                         }
4882                         OBJ(upspeedf) {
4883                                 spaced_print(p, p_max_size, "%.1f", 8,
4884                                         obj->data.net->trans_speed / 1024.0);
4885                         }
4886 #ifdef X11
4887                         OBJ(upspeedgraph) {
4888                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
4889                                         obj->data.net->trans_speed / 1024.0, obj->e, 1, obj->char_a, obj->char_b);
4890                         }
4891 #endif /* X11 */
4892                         OBJ(uptime_short) {
4893                                 format_seconds_short(p, p_max_size, (int) cur->uptime);
4894                         }
4895                         OBJ(uptime) {
4896                                 format_seconds(p, p_max_size, (int) cur->uptime);
4897                         }
4898                         OBJ(user_names) {
4899                                 snprintf(p, p_max_size, "%s", cur->users.names);
4900                         }
4901                         OBJ(user_terms) {
4902                                 snprintf(p, p_max_size, "%s", cur->users.terms);
4903                         }
4904                         OBJ(user_times) {
4905                                 snprintf(p, p_max_size, "%s", cur->users.times);
4906                         }
4907                         OBJ(user_number) {
4908                                 snprintf(p, p_max_size, "%d", cur->users.number);
4909                         }
4910 #if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \
4911                 || defined(__OpenBSD__)) && (defined(i386) || defined(__i386__))
4912                         OBJ(apm_adapter) {
4913                                 char *msg;
4914
4915                                 msg = get_apm_adapter();
4916                                 snprintf(p, p_max_size, "%s", msg);
4917                                 free(msg);
4918                         }
4919                         OBJ(apm_battery_life) {
4920                                 char *msg;
4921
4922                                 msg = get_apm_battery_life();
4923                                 snprintf(p, p_max_size, "%s", msg);
4924                                 free(msg);
4925                         }
4926                         OBJ(apm_battery_time) {
4927                                 char *msg;
4928
4929                                 msg = get_apm_battery_time();
4930                                 snprintf(p, p_max_size, "%s", msg);
4931                                 free(msg);
4932                         }
4933 #endif /* __FreeBSD__ __OpenBSD__ */
4934
4935 #ifdef MPD
4936 #define mpd_printf(fmt, val) \
4937         snprintf(p, p_max_size, fmt, mpd_get_info()->val)
4938 #define mpd_sprintf(val) { \
4939         if (!obj->data.i || obj->data.i > p_max_size) \
4940                 mpd_printf("%s", val); \
4941         else \
4942                 snprintf(p, obj->data.i, "%s", mpd_get_info()->val); \
4943 }
4944                         OBJ(mpd_title)
4945                                 mpd_sprintf(title);
4946                         OBJ(mpd_artist)
4947                                 mpd_sprintf(artist);
4948                         OBJ(mpd_album)
4949                                 mpd_sprintf(album);
4950                         OBJ(mpd_random)
4951                                 mpd_printf("%s", random);
4952                         OBJ(mpd_repeat)
4953                                 mpd_printf("%s", repeat);
4954                         OBJ(mpd_track)
4955                                 mpd_sprintf(track);
4956                         OBJ(mpd_name)
4957                                 mpd_sprintf(name);
4958                         OBJ(mpd_file)
4959                                 mpd_sprintf(file);
4960                         OBJ(mpd_vol)
4961                                 mpd_printf("%d", volume);
4962                         OBJ(mpd_bitrate)
4963                                 mpd_printf("%d", bitrate);
4964                         OBJ(mpd_status)
4965                                 mpd_printf("%s", status);
4966                         OBJ(mpd_elapsed) {
4967                                 format_media_player_time(p, p_max_size, mpd_get_info()->elapsed);
4968                         }
4969                         OBJ(mpd_length) {
4970                                 format_media_player_time(p, p_max_size, mpd_get_info()->length);
4971                         }
4972                         OBJ(mpd_percent) {
4973                                 percent_print(p, p_max_size, (int)(mpd_get_info()->progress * 100));
4974                         }
4975                         OBJ(mpd_bar) {
4976 #ifdef X11
4977                                 if(output_methods & TO_X) {
4978                                         new_bar(p, obj->data.pair.a, obj->data.pair.b,
4979                                                 (int) (mpd_get_info()->progress * 255.0f));
4980                                 } else {
4981 #endif /* X11 */
4982                                         if(!obj->data.pair.a) obj->data.pair.a = DEFAULT_BAR_WIDTH_NO_X;
4983                                         new_bar_in_shell(p, p_max_size, (int) (mpd_get_info()->progress * 100.0f), obj->data.pair.a);
4984 #ifdef X11
4985                                 }
4986 #endif /* X11 */
4987                         }
4988                         OBJ(mpd_smart) {
4989                                 struct mpd_s *mpd = mpd_get_info();
4990                                 int len = obj->data.i;
4991                                 if (len == 0 || len > p_max_size)
4992                                         len = p_max_size;
4993
4994                                 memset(p, 0, p_max_size);
4995                                 if (mpd->artist && *mpd->artist &&
4996                                     mpd->title && *mpd->title) {
4997                                         snprintf(p, len, "%s - %s", mpd->artist,
4998                                                 mpd->title);
4999                                 } else if (mpd->title && *mpd->title) {
5000                                         snprintf(p, len, "%s", mpd->title);
5001                                 } else if (mpd->artist && *mpd->artist) {
5002                                         snprintf(p, len, "%s", mpd->artist);
5003                                 } else if (mpd->file && *mpd->file) {
5004                                         snprintf(p, len, "%s", mpd->file);
5005                                 } else {
5006                                         *p = 0;
5007                                 }
5008                         }
5009                         OBJ(if_mpd_playing) {
5010                                 if (!mpd_get_info()->is_playing) {
5011                                         DO_JUMP;
5012                                 }
5013                         }
5014 #undef mpd_sprintf
5015 #undef mpd_printf
5016 #endif
5017
5018 #ifdef XMMS2
5019     free_xmms2();
5020 #endif
5021
5022 #ifdef MOC
5023 #define MOC_PRINT(t, a) \
5024         snprintf(p, p_max_size, "%s", (moc.t ? moc.t : a))
5025                         OBJ(moc_state) {
5026                                 MOC_PRINT(state, "??");
5027                         }
5028                         OBJ(moc_file) {
5029                                 MOC_PRINT(file, "no file");
5030                         }
5031                         OBJ(moc_title) {
5032                                 MOC_PRINT(title, "no title");
5033                         }
5034                         OBJ(moc_artist) {
5035                                 MOC_PRINT(artist, "no artist");
5036                         }
5037                         OBJ(moc_song) {
5038                                 MOC_PRINT(song, "no song");
5039                         }
5040                         OBJ(moc_album) {
5041                                 MOC_PRINT(album, "no album");
5042                         }
5043                         OBJ(moc_totaltime) {
5044                                 MOC_PRINT(totaltime, "0:00");
5045                         }
5046                         OBJ(moc_timeleft) {
5047                                 MOC_PRINT(timeleft, "0:00");
5048                         }
5049                         OBJ(moc_curtime) {
5050                                 MOC_PRINT(curtime, "0:00");
5051                         }
5052                         OBJ(moc_bitrate) {
5053                                 MOC_PRINT(bitrate, "0Kbps");
5054                         }
5055                         OBJ(moc_rate) {
5056                                 MOC_PRINT(rate, "0KHz");
5057                         }
5058 #undef MOC_PRINT
5059 #endif /* MOC */
5060 #ifdef XMMS2
5061                         OBJ(xmms2_artist) {
5062                                 snprintf(p, p_max_size, "%s", cur->xmms2.artist);
5063                         }
5064                         OBJ(xmms2_album) {
5065                                 snprintf(p, p_max_size, "%s", cur->xmms2.album);
5066                         }
5067                         OBJ(xmms2_title) {
5068                                 snprintf(p, p_max_size, "%s", cur->xmms2.title);
5069                         }
5070                         OBJ(xmms2_genre) {
5071                                 snprintf(p, p_max_size, "%s", cur->xmms2.genre);
5072                         }
5073                         OBJ(xmms2_comment) {
5074                                 snprintf(p, p_max_size, "%s", cur->xmms2.comment);
5075                         }
5076                         OBJ(xmms2_url) {
5077                                 snprintf(p, p_max_size, "%s", cur->xmms2.url);
5078                         }
5079                         OBJ(xmms2_status) {
5080                                 snprintf(p, p_max_size, "%s", cur->xmms2.status);
5081                         }
5082                         OBJ(xmms2_date) {
5083                                 snprintf(p, p_max_size, "%s", cur->xmms2.date);
5084                         }
5085                         OBJ(xmms2_tracknr) {
5086                                 if (cur->xmms2.tracknr != -1) {
5087                                         snprintf(p, p_max_size, "%i", cur->xmms2.tracknr);
5088                                 }
5089                         }
5090                         OBJ(xmms2_bitrate) {
5091                                 snprintf(p, p_max_size, "%i", cur->xmms2.bitrate);
5092                         }
5093                         OBJ(xmms2_id) {
5094                                 snprintf(p, p_max_size, "%u", cur->xmms2.id);
5095                         }
5096                         OBJ(xmms2_size) {
5097                                 snprintf(p, p_max_size, "%2.1f", cur->xmms2.size);
5098                         }
5099                         OBJ(xmms2_elapsed) {
5100                                 snprintf(p, p_max_size, "%02d:%02d", cur->xmms2.elapsed / 60000,
5101                                         (cur->xmms2.elapsed / 1000) % 60);
5102                         }
5103                         OBJ(xmms2_duration) {
5104                                 snprintf(p, p_max_size, "%02d:%02d",
5105                                         cur->xmms2.duration / 60000,
5106                                         (cur->xmms2.duration / 1000) % 60);
5107                         }
5108                         OBJ(xmms2_percent) {
5109                                 snprintf(p, p_max_size, "%2.0f", cur->xmms2.progress * 100);
5110                         }
5111                         OBJ(xmms2_bar) {
5112                                 new_bar(p, obj->data.pair.a, obj->data.pair.b,
5113                                         (int) (cur->xmms2.progress * 255.0f));
5114                         }
5115                         OBJ(xmms2_playlist) {
5116                                 snprintf(p, p_max_size, "%s", cur->xmms2.playlist);
5117                         }
5118                         OBJ(xmms2_timesplayed) {
5119                                 snprintf(p, p_max_size, "%i", cur->xmms2.timesplayed);
5120                         }
5121                         OBJ(xmms2_smart) {
5122                                 if (strlen(cur->xmms2.title) < 2
5123                                                 && strlen(cur->xmms2.title) < 2) {
5124                                         snprintf(p, p_max_size, "%s", cur->xmms2.url);
5125                                 } else {
5126                                         snprintf(p, p_max_size, "%s - %s", cur->xmms2.artist,
5127                                                 cur->xmms2.title);
5128                                 }
5129                         }
5130                         OBJ(if_xmms2_connected) {
5131                                 if (cur->xmms2.conn_state != 1) {
5132                                         DO_JUMP;
5133                                 }
5134                         }
5135 #endif /* XMMS */
5136 #ifdef AUDACIOUS
5137                         OBJ(audacious_status) {
5138                                 snprintf(p, p_max_size, "%s",
5139                                         cur->audacious.items[AUDACIOUS_STATUS]);
5140                         }
5141                         OBJ(audacious_title) {
5142                                 snprintf(p, cur->audacious.max_title_len > 0
5143                                         ? cur->audacious.max_title_len : p_max_size, "%s",
5144                                         cur->audacious.items[AUDACIOUS_TITLE]);
5145                         }
5146                         OBJ(audacious_length) {
5147                                 snprintf(p, p_max_size, "%s",
5148                                         cur->audacious.items[AUDACIOUS_LENGTH]);
5149                         }
5150                         OBJ(audacious_length_seconds) {
5151                                 snprintf(p, p_max_size, "%s",
5152                                         cur->audacious.items[AUDACIOUS_LENGTH_SECONDS]);
5153                         }
5154                         OBJ(audacious_position) {
5155                                 snprintf(p, p_max_size, "%s",
5156                                         cur->audacious.items[AUDACIOUS_POSITION]);
5157                         }
5158                         OBJ(audacious_position_seconds) {
5159                                 snprintf(p, p_max_size, "%s",
5160                                         cur->audacious.items[AUDACIOUS_POSITION_SECONDS]);
5161                         }
5162                         OBJ(audacious_bitrate) {
5163                                 snprintf(p, p_max_size, "%s",
5164                                         cur->audacious.items[AUDACIOUS_BITRATE]);
5165                         }
5166                         OBJ(audacious_frequency) {
5167                                 snprintf(p, p_max_size, "%s",
5168                                         cur->audacious.items[AUDACIOUS_FREQUENCY]);
5169                         }
5170                         OBJ(audacious_channels) {
5171                                 snprintf(p, p_max_size, "%s",
5172                                         cur->audacious.items[AUDACIOUS_CHANNELS]);
5173                         }
5174                         OBJ(audacious_filename) {
5175                                 snprintf(p, p_max_size, "%s",
5176                                         cur->audacious.items[AUDACIOUS_FILENAME]);
5177                         }
5178                         OBJ(audacious_playlist_length) {
5179                                 snprintf(p, p_max_size, "%s",
5180                                         cur->audacious.items[AUDACIOUS_PLAYLIST_LENGTH]);
5181                         }
5182                         OBJ(audacious_playlist_position) {
5183                                 snprintf(p, p_max_size, "%s",
5184                                         cur->audacious.items[AUDACIOUS_PLAYLIST_POSITION]);
5185                         }
5186                         OBJ(audacious_main_volume) {
5187                                 snprintf(p, p_max_size, "%s",
5188                                         cur->audacious.items[AUDACIOUS_MAIN_VOLUME]);
5189                         }
5190                         OBJ(audacious_bar) {
5191                                 double progress;
5192
5193                                 progress =
5194                                         atof(cur->audacious.items[AUDACIOUS_POSITION_SECONDS]) /
5195                                         atof(cur->audacious.items[AUDACIOUS_LENGTH_SECONDS]);
5196                                 new_bar(p, obj->a, obj->b, (int) (progress * 255.0f));
5197                         }
5198 #endif /* AUDACIOUS */
5199
5200 #ifdef BMPX
5201                         OBJ(bmpx_title) {
5202                                 snprintf(p, p_max_size, "%s", cur->bmpx.title);
5203                         }
5204                         OBJ(bmpx_artist) {
5205                                 snprintf(p, p_max_size, "%s", cur->bmpx.artist);
5206                         }
5207                         OBJ(bmpx_album) {
5208                                 snprintf(p, p_max_size, "%s", cur->bmpx.album);
5209                         }
5210                         OBJ(bmpx_uri) {
5211                                 snprintf(p, p_max_size, "%s", cur->bmpx.uri);
5212                         }
5213                         OBJ(bmpx_track) {
5214                                 snprintf(p, p_max_size, "%i", cur->bmpx.track);
5215                         }
5216                         OBJ(bmpx_bitrate) {
5217                                 snprintf(p, p_max_size, "%i", cur->bmpx.bitrate);
5218                         }
5219 #endif /* BMPX */
5220                         /* we have three different types of top (top, top_mem
5221                          * and top_time). To avoid having almost-same code three
5222                          * times, we have this special handler. */
5223                         break;
5224                         case OBJ_top:
5225                                 parse_top_args("top", obj->data.top.s, obj);
5226                                 if (!needed) needed = cur->cpu;
5227                         case OBJ_top_mem:
5228                                 parse_top_args("top_mem", obj->data.top.s, obj);
5229                                 if (!needed) needed = cur->memu;
5230                         case OBJ_top_time:
5231                                 parse_top_args("top_time", obj->data.top.s, obj);
5232                                 if (!needed) needed = cur->time;
5233
5234                                 if (needed[obj->data.top.num]) {
5235                                         char *timeval;
5236
5237                                         switch (obj->data.top.type) {
5238                                                 case TOP_NAME:
5239                                                         snprintf(p, top_name_width + 1, "%-*s", top_name_width,
5240                                                                         needed[obj->data.top.num]->name);
5241                                                         break;
5242                                                 case TOP_CPU:
5243                                                         snprintf(p, 7, "%6.2f",
5244                                                                         needed[obj->data.top.num]->amount);
5245                                                         break;
5246                                                 case TOP_PID:
5247                                                         snprintf(p, 6, "%5i",
5248                                                                         needed[obj->data.top.num]->pid);
5249                                                         break;
5250                                                 case TOP_MEM:
5251                                                         snprintf(p, 7, "%6.2f",
5252                                                                         needed[obj->data.top.num]->totalmem);
5253                                                         break;
5254                                                 case TOP_TIME:
5255                                                         timeval = format_time(
5256                                                                         needed[obj->data.top.num]->total_cpu_time, 9);
5257                                                         snprintf(p, 10, "%9s", timeval);
5258                                                         free(timeval);
5259                                                         break;
5260                                                 case TOP_MEM_RES:
5261                                                         human_readable(needed[obj->data.top.num]->rss,
5262                                                                         p, 255);
5263                                                         break;
5264                                                 case TOP_MEM_VSIZE:
5265                                                         human_readable(needed[obj->data.top.num]->vsize,
5266                                                                         p, 255);
5267                                                         break;
5268                                         }
5269                                 }
5270                         OBJ(tail)
5271                                 print_tail_object(obj, p, p_max_size);
5272                         OBJ(head)
5273                                 print_head_object(obj, p, p_max_size);
5274                         OBJ(lines) {
5275                                 FILE *fp = open_file(obj->data.s, &obj->a);
5276
5277                                 if(fp != NULL) {
5278 /* FIXME: use something more general (see also tail.c, head.c */
5279 #define BUFSZ 0x1000
5280                                         char buf[BUFSZ];
5281                                         int j, lines;
5282
5283                                         lines = 0;
5284                                         while(fgets(buf, BUFSZ, fp) != NULL){
5285                                                 for(j = 0; buf[j] != 0; j++) {
5286                                                         if(buf[j] == '\n') {
5287                                                                 lines++;
5288                                                         }
5289                                                 }
5290                                         }
5291                                         sprintf(p, "%d", lines);
5292                                         fclose(fp);
5293                                 } else {
5294                                         sprintf(p, "File Unreadable");
5295                                 }
5296                         }
5297
5298                         OBJ(words) {
5299                                 FILE *fp = open_file(obj->data.s, &obj->a);
5300
5301                                 if(fp != NULL) {
5302                                         char buf[BUFSZ];
5303                                         int j, words;
5304                                         char inword = FALSE;
5305
5306                                         words = 0;
5307                                         while(fgets(buf, BUFSZ, fp) != NULL){
5308                                                 for(j = 0; buf[j] != 0; j++) {
5309                                                         if(!isspace(buf[j])) {
5310                                                                 if(inword == FALSE) {
5311                                                                         words++;
5312                                                                         inword = TRUE;
5313                                                                 }
5314                                                         } else {
5315                                                                 inword = FALSE;
5316                                                         }
5317                                                 }
5318                                         }
5319                                         sprintf(p, "%d", words);
5320                                         fclose(fp);
5321                                 } else {
5322                                         sprintf(p, "File Unreadable");
5323                                 }
5324                         }
5325 #ifdef TCP_PORT_MONITOR
5326                         OBJ(tcp_portmon) {
5327                                 tcp_portmon_action(p, p_max_size,
5328                                                    &obj->data.tcp_port_monitor);
5329                         }
5330 #endif /* TCP_PORT_MONITOR */
5331
5332 #ifdef HAVE_ICONV
5333                         OBJ(iconv_start) {
5334                                 iconv_converting = 1;
5335                                 iconv_selected = obj->a;
5336                         }
5337                         OBJ(iconv_stop) {
5338                                 iconv_converting = 0;
5339                                 iconv_selected = 0;
5340                         }
5341 #endif /* HAVE_ICONV */
5342
5343                         OBJ(entropy_avail) {
5344                                 snprintf(p, p_max_size, "%d", cur->entropy.entropy_avail);
5345                         }
5346                         OBJ(entropy_poolsize) {
5347                                 snprintf(p, p_max_size, "%d", cur->entropy.poolsize);
5348                         }
5349                         OBJ(entropy_bar) {
5350                                 double entropy_perc;
5351
5352                                 entropy_perc = (double) cur->entropy.entropy_avail /
5353                                         (double) cur->entropy.poolsize;
5354 #ifdef X11
5355                                 if(output_methods & TO_X) {
5356                                         new_bar(p, obj->a, obj->b, (int) (entropy_perc * 255.0f));
5357                                 } else {
5358 #endif /* X11 */
5359                                         if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
5360                                         new_bar_in_shell(p, p_max_size, (int) (entropy_perc * 100.0f), obj->a);
5361 #ifdef X11
5362                                 }
5363 #endif /* X11 */
5364                         }
5365 #ifdef IBM
5366                         OBJ(smapi) {
5367                                 char *s;
5368                                 if(obj->data.s) {
5369                                         s = smapi_get_val(obj->data.s);
5370                                         snprintf(p, p_max_size, "%s", s);
5371                                         free(s);
5372                                 }
5373                         }
5374                         OBJ(if_smapi_bat_installed) {
5375                                 int idx;
5376                                 if(obj->data.ifblock.s && sscanf(obj->data.ifblock.s, "%i", &idx) == 1) {
5377                                         if(!smapi_bat_installed(idx)) {
5378                                                 DO_JUMP;
5379                                         }
5380                                 } else
5381                                         ERR("argument to if_smapi_bat_installed must be an integer");
5382                         }
5383                         OBJ(smapi_bat_perc) {
5384                                 int idx, val;
5385                                 if(obj->data.s && sscanf(obj->data.s, "%i", &idx) == 1) {
5386                                         val = smapi_bat_installed(idx) ?
5387                                                 smapi_get_bat_int(idx, "remaining_percent") : 0;
5388                                         percent_print(p, p_max_size, val);
5389                                 } else
5390                                         ERR("argument to smapi_bat_perc must be an integer");
5391                         }
5392                         OBJ(smapi_bat_temp) {
5393                                 int idx, val;
5394                                 if(obj->data.s && sscanf(obj->data.s, "%i", &idx) == 1) {
5395                                         val = smapi_bat_installed(idx) ?
5396                                                 smapi_get_bat_int(idx, "temperature") : 0;
5397                                         /* temperature is in milli degree celsius */
5398                                         temp_print(p, p_max_size, val / 1000, TEMP_CELSIUS);
5399                                 } else
5400                                         ERR("argument to smapi_bat_temp must be an integer");
5401                         }
5402                         OBJ(smapi_bat_power) {
5403                                 int idx, val;
5404                                 if(obj->data.s && sscanf(obj->data.s, "%i", &idx) == 1) {
5405                                         val = smapi_bat_installed(idx) ?
5406                                                 smapi_get_bat_int(idx, "power_now") : 0;
5407                                         /* power_now is in mW, set to W with one digit precision */
5408                                         snprintf(p, p_max_size, "%.1f", ((double)val / 1000));
5409                                 } else
5410                                         ERR("argument to smapi_bat_power must be an integer");
5411                         }
5412                         OBJ(smapi_bat_bar) {
5413                                 if(obj->data.i >= 0 && smapi_bat_installed(obj->data.i))
5414                                         new_bar(p, obj->a, obj->b, (int)
5415                                                         (255 * smapi_get_bat_int(obj->data.i, "remaining_percent") / 100));
5416                                 else
5417                                         new_bar(p, obj->a, obj->b, 0);
5418                         }
5419 #endif /* IBM */
5420                         OBJ(scroll) {
5421                                 unsigned int j;
5422                                 char *tmp, buf[max_user_text];
5423                                 generate_text_internal(buf, max_user_text,
5424                                                        *obj->sub, cur);
5425
5426                                 if (strlen(buf) <= obj->data.scroll.show) {
5427                                         snprintf(p, p_max_size, "%s", buf);
5428                                         break;
5429                                 }
5430 #define LINESEPARATOR '|'
5431                                 //place all the lines behind each other with LINESEPARATOR between them
5432                                 for(j = 0; buf[j] != 0; j++) {
5433                                         if(buf[j]=='\n') {
5434                                                 buf[j]=LINESEPARATOR;
5435                                         }
5436                                 }
5437                                 //scroll the output obj->data.scroll.start places by copying that many chars from
5438                                 //the front of the string to tmp, scrolling the rest to the front and placing tmp
5439                                 //at the back of the string
5440                                 tmp = calloc(obj->data.scroll.start + 1, sizeof(char));
5441                                 strncpy(tmp, buf, obj->data.scroll.start); tmp[obj->data.scroll.start] = 0;
5442                                 for(j = obj->data.scroll.start; buf[j] != 0; j++){
5443                                         buf[j - obj->data.scroll.start] = buf[j];
5444                                 }
5445                                 strcpy(&buf[j - obj->data.scroll.start], tmp);
5446                                 free(tmp);
5447                                 //only show the requested number of chars
5448                                 if(obj->data.scroll.show < j) {
5449                                         buf[obj->data.scroll.show] = 0;
5450                                 }
5451                                 //next time, scroll a place more or reset scrolling if we are at the end
5452                                 obj->data.scroll.start += obj->data.scroll.step;
5453                                 if(obj->data.scroll.start >= j){
5454                                          obj->data.scroll.start = 0;
5455                                 }
5456                                 snprintf(p, p_max_size, "%s", buf);
5457                         }
5458                         OBJ(combine) {
5459                                 char buf[2][max_user_text];
5460                                 int i, j;
5461                                 long longest=0;
5462                                 int nextstart;
5463                                 int nr_rows[2];
5464                                 struct llrows {
5465                                         char* row;
5466                                         struct llrows* next;
5467                                 };
5468                                 struct llrows *ll_rows[2], *current[2];
5469                                 struct text_object * objsub = obj->sub;
5470
5471                                 p[0]=0;
5472                                 #ifdef HAVE_OPENMP
5473                                 #pragma omp parallel for
5474                                 #endif /* HAVE_OPENMP */
5475                                 for(i=0; i<2; i++) {
5476                                         nr_rows[i] = 1;
5477                                         nextstart = 0;
5478                                         ll_rows[i] = malloc(sizeof(struct llrows));
5479                                         current[i] = ll_rows[i];
5480                                         #ifdef HAVE_OPENMP
5481                                         #pragma omp parallel for
5482                                         #endif /* HAVE_OPENMP */
5483                                         for(j=0; j<i; j++) objsub = objsub->sub;
5484                                         generate_text_internal(buf[i], max_user_text, *objsub, cur);
5485                                         /* doesnut work
5486                                          * #ifdef HAVE_OPENMP
5487                                          * #pragma omp parallel for reduction (+:nr_rows[i])
5488                                          * #endif  HAVE_OPENMP */
5489                                         for(j=0; buf[i][j] != 0; j++) {
5490                                                 if(buf[i][j] == '\t') buf[i][j] = ' ';
5491                                                 if(buf[i][j] == '\n') {
5492                                                         buf[i][j] = 0;
5493                                                         current[i]->row = strdup(buf[i]+nextstart);
5494                                                         if(i==0 && (long)strlen(current[i]->row) > longest) longest = (long)strlen(current[i]->row);
5495                                                         current[i]->next = malloc(sizeof(struct llrows));
5496                                                         current[i] = current[i]->next;
5497                                                         nextstart = j + 1;
5498                                                         nr_rows[i]++;
5499                                                 }
5500                                         }
5501                                         current[i]->row = strdup(buf[i]+nextstart);
5502                                         if(i==0 && (long)strlen(current[i]->row) > longest) longest = (long)strlen(current[i]->row);
5503                                         current[i]->next = NULL;
5504                                         current[i] = ll_rows[i];
5505                                 }
5506                                 for(j=0; j < (nr_rows[0] > nr_rows[1] ? nr_rows[0] : nr_rows[1] ); j++) {
5507                                         if(current[0]) {
5508                                                 strcat(p, current[0]->row);
5509                                                 i=strlen(current[0]->row);
5510                                         }else i = 0;
5511                                         while(i < longest) {
5512                                                 strcat(p, " ");
5513                                                 i++;
5514                                         }
5515                                         if(current[1]) {
5516                                                 strcat(p, obj->data.combine.seperation);
5517                                                 strcat(p, current[1]->row);
5518                                         }
5519                                         strcat(p, "\n");
5520                                         #ifdef HAVE_OPENMP
5521                                         #pragma omp parallel for
5522                                         #endif /* HAVE_OPENMP */
5523                                         for(i=0; i<2; i++) if(current[i]) current[i]=current[i]->next;
5524                                 }
5525                                 #ifdef HAVE_OPENMP
5526                                 #pragma omp parallel for
5527                                 #endif /* HAVE_OPENMP */
5528                                 for(i=0; i<2; i++) {
5529                                         while(ll_rows[i] != NULL) {
5530                                                 current[i]=ll_rows[i];
5531                                                 free(current[i]->row);
5532                                                 ll_rows[i]=current[i]->next;
5533                                                 free(current[i]);
5534                                         }
5535                                 }
5536                         }
5537 #ifdef NVIDIA
5538                         OBJ(nvidia) {
5539                                 int value = get_nvidia_value(obj->data.nvidia.type, display);
5540                                 if(value == -1)
5541                                         snprintf(p, p_max_size, "N/A");
5542                                 else if (obj->data.nvidia.type == NV_TEMP)
5543                                         temp_print(p, p_max_size, (double)value, TEMP_CELSIUS);
5544                                 else if (obj->data.nvidia.print_as_float &&
5545                                                 value > 0 && value < 100)
5546                                         snprintf(p, p_max_size, "%.1f", (float)value);
5547                                 else
5548                                         snprintf(p, p_max_size, "%d", value);
5549                         }
5550 #endif /* NVIDIA */
5551 #ifdef APCUPSD
5552                         OBJ(apcupsd) {
5553                                 /* This is just a meta-object to set host:port */
5554                         }
5555                         OBJ(apcupsd_name) {
5556                                 snprintf(p, p_max_size, "%s",
5557                                                  cur->apcupsd.items[APCUPSD_NAME]);
5558                         }
5559                         OBJ(apcupsd_model) {
5560                                 snprintf(p, p_max_size, "%s",
5561                                                  cur->apcupsd.items[APCUPSD_MODEL]);
5562                         }
5563                         OBJ(apcupsd_upsmode) {
5564                                 snprintf(p, p_max_size, "%s",
5565                                                  cur->apcupsd.items[APCUPSD_UPSMODE]);
5566                         }
5567                         OBJ(apcupsd_cable) {
5568                                 snprintf(p, p_max_size, "%s",
5569                                                  cur->apcupsd.items[APCUPSD_CABLE]);
5570                         }
5571                         OBJ(apcupsd_status) {
5572                                 snprintf(p, p_max_size, "%s",
5573                                                  cur->apcupsd.items[APCUPSD_STATUS]);
5574                         }
5575                         OBJ(apcupsd_linev) {
5576                                 snprintf(p, p_max_size, "%s",
5577                                                  cur->apcupsd.items[APCUPSD_LINEV]);
5578                         }
5579                         OBJ(apcupsd_load) {
5580                                 snprintf(p, p_max_size, "%s",
5581                                                  cur->apcupsd.items[APCUPSD_LOAD]);
5582                         }
5583                         OBJ(apcupsd_loadbar) {
5584                                 double progress;
5585 #ifdef X11
5586                                 if(output_methods & TO_X) {
5587                                         progress = atof(cur->apcupsd.items[APCUPSD_LOAD]) / 100.0 * 255.0;
5588                                         new_bar(p, obj->a, obj->b, (int) progress);
5589                                 } else {
5590 #endif /* X11 */
5591                                         progress = atof(cur->apcupsd.items[APCUPSD_LOAD]);
5592                                         if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
5593                                         new_bar_in_shell(p, p_max_size, (int) progress, obj->a);
5594 #ifdef X11
5595                                 }
5596 #endif /* X11 */
5597                         }
5598 #ifdef X11
5599                         OBJ(apcupsd_loadgraph) {
5600                                 double progress;
5601                                 progress =      atof(cur->apcupsd.items[APCUPSD_LOAD]);
5602                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
5603                                                   (int)progress, 100, 1, obj->char_a, obj->char_b);
5604                         }
5605                         OBJ(apcupsd_loadgauge) {
5606                                 double progress;
5607                                 progress =      atof(cur->apcupsd.items[APCUPSD_LOAD]) / 100.0 * 255.0;
5608                                 new_gauge(p, obj->a, obj->b,
5609                                                   (int)progress);
5610                         }
5611 #endif /* X11 */
5612                         OBJ(apcupsd_charge) {
5613                                 snprintf(p, p_max_size, "%s",
5614                                                  cur->apcupsd.items[APCUPSD_CHARGE]);
5615                         }
5616                         OBJ(apcupsd_timeleft) {
5617                                 snprintf(p, p_max_size, "%s",
5618                                                  cur->apcupsd.items[APCUPSD_TIMELEFT]);
5619                         }
5620                         OBJ(apcupsd_temp) {
5621                                 snprintf(p, p_max_size, "%s",
5622                                                  cur->apcupsd.items[APCUPSD_TEMP]);
5623                         }
5624                         OBJ(apcupsd_lastxfer) {
5625                                 snprintf(p, p_max_size, "%s",
5626                                                  cur->apcupsd.items[APCUPSD_LASTXFER]);
5627                         }
5628 #endif /* APCUPSD */
5629                         break;
5630                 }
5631 #undef DO_JUMP
5632
5633
5634                 {
5635                         unsigned int a = strlen(p);
5636
5637 #ifdef HAVE_ICONV
5638                         if (a > 0 && iconv_converting && iconv_selected > 0
5639                                         && (iconv_cd[iconv_selected - 1] != (iconv_t) (-1))) {
5640                                 int bytes;
5641                                 size_t dummy1, dummy2;
5642                                 char *ptr = buff_in;
5643                                 char *outptr = p;
5644
5645                                 dummy1 = dummy2 = a;
5646
5647                                 strncpy(buff_in, p, p_max_size);
5648
5649                                 iconv(*iconv_cd[iconv_selected - 1], NULL, NULL, NULL, NULL);
5650                                 while (dummy1 > 0) {
5651                                         bytes = iconv(*iconv_cd[iconv_selected - 1], &ptr, &dummy1,
5652                                                         &outptr, &dummy2);
5653                                         if (bytes == -1) {
5654                                                 ERR("Iconv codeset conversion failed");
5655                                                 break;
5656                                         }
5657                                 }
5658
5659                                 /* It is nessecary when we are converting from multibyte to
5660                                  * singlebyte codepage */
5661                                 a = outptr - p;
5662                         }
5663 #endif /* HAVE_ICONV */
5664                         p += a;
5665                         p_max_size -= a;
5666                 }
5667                 obj = obj->next;
5668         }
5669 }
5670
5671 double current_update_time, next_update_time, last_update_time;
5672
5673 static void generate_text(void)
5674 {
5675         struct information *cur = &info;
5676         char *p;
5677
5678         special_count = 0;
5679
5680         /* update info */
5681
5682         current_update_time = get_time();
5683
5684         update_stuff();
5685
5686         /* add things to the buffer */
5687
5688         /* generate text */
5689
5690         p = text_buffer;
5691
5692         generate_text_internal(p, max_user_text, global_root_object, cur);
5693
5694         if (stuff_in_upper_case) {
5695                 char *tmp_p;
5696
5697                 tmp_p = text_buffer;
5698                 while (*tmp_p) {
5699                         *tmp_p = toupper(*tmp_p);
5700                         tmp_p++;
5701                 }
5702         }
5703
5704         next_update_time += update_interval;
5705         if (next_update_time < get_time()) {
5706                 next_update_time = get_time() + update_interval;
5707         } else if (next_update_time > get_time() + update_interval) {
5708                 next_update_time = get_time() + update_interval;
5709         }
5710         last_update_time = current_update_time;
5711         total_updates++;
5712         // free(p);
5713 }
5714
5715 static inline int get_string_width(const char *s)
5716 {
5717 #ifdef X11
5718         if (output_methods & TO_X) {
5719                 return *s ? calc_text_width(s, strlen(s)) : 0;
5720         }
5721 #endif /* X11 */
5722         return strlen(s);
5723 }
5724
5725 static inline int get_string_width_special(char *s)
5726 {
5727 #ifdef X11
5728         char *p, *final;
5729         int idx = 1;
5730         int width = 0;
5731         long i;
5732
5733         if ((output_methods & TO_X) == 0) {
5734 #endif
5735                 return (s) ? strlen(s) : 0;
5736 #ifdef X11
5737         }
5738
5739         if (!s) {
5740                 return 0;
5741         }
5742
5743         p = strndup(s, text_buffer_size);
5744         final = p;
5745
5746         while (*p) {
5747                 if (*p == SPECIAL_CHAR) {
5748                         /* shift everything over by 1 so that the special char
5749                          * doesn't mess up the size calculation */
5750                         for (i = 0; i < (long)strlen(p); i++) {
5751                                 *(p + i) = *(p + i + 1);
5752                         }
5753                         if (specials[special_index + idx].type == GRAPH
5754                                         || specials[special_index + idx].type == GAUGE
5755                                         || specials[special_index + idx].type == BAR) {
5756                                 width += specials[special_index + idx].width;
5757                         }
5758                         idx++;
5759                 } else {
5760                         p++;
5761                 }
5762         }
5763         if (strlen(final) > 1) {
5764                 width += calc_text_width(final, strlen(final));
5765         }
5766         free(final);
5767         return width;
5768 #endif /* X11 */
5769 }
5770
5771 #ifdef X11
5772 static void text_size_updater(char *s);
5773
5774 int last_font_height;
5775 static void update_text_area(void)
5776 {
5777         int x = 0, y = 0;
5778
5779         if ((output_methods & TO_X) == 0)
5780                 return;
5781         /* update text size if it isn't fixed */
5782 #ifdef OWN_WINDOW
5783         if (!fixed_size)
5784 #endif
5785         {
5786                 text_width = minimum_width;
5787                 text_height = 0;
5788                 special_index = 0;
5789                 last_font_height = font_height();
5790                 for_each_line(text_buffer, text_size_updater);
5791                 text_width += 1;
5792                 if (text_height < minimum_height) {
5793                         text_height = minimum_height;
5794                 }
5795                 if (text_width > maximum_width && maximum_width > 0) {
5796                         text_width = maximum_width;
5797                 }
5798         }
5799
5800         /* get text position on workarea */
5801         switch (text_alignment) {
5802                 case TOP_LEFT:
5803                         x = gap_x;
5804                         y = gap_y;
5805                         break;
5806
5807                 case TOP_RIGHT:
5808                         x = workarea[2] - text_width - gap_x;
5809                         y = gap_y;
5810                         break;
5811
5812                 case TOP_MIDDLE:
5813                         x = workarea[2] / 2 - text_width / 2 - gap_x;
5814                         y = gap_y;
5815                         break;
5816
5817                 default:
5818                 case BOTTOM_LEFT:
5819                         x = gap_x;
5820                         y = workarea[3] - text_height - gap_y;
5821                         break;
5822
5823                 case BOTTOM_RIGHT:
5824                         x = workarea[2] - text_width - gap_x;
5825                         y = workarea[3] - text_height - gap_y;
5826                         break;
5827
5828                 case BOTTOM_MIDDLE:
5829                         x = workarea[2] / 2 - text_width / 2 - gap_x;
5830                         y = workarea[3] - text_height - gap_y;
5831                         break;
5832
5833                 case MIDDLE_LEFT:
5834                         x = gap_x;
5835                         y = workarea[3] / 2 - text_height / 2 - gap_y;
5836                         break;
5837
5838                 case MIDDLE_RIGHT:
5839                         x = workarea[2] - text_width - gap_x;
5840                         y = workarea[3] / 2 - text_height / 2 - gap_y;
5841                         break;
5842
5843 #ifdef OWN_WINDOW
5844                 case NONE:      // Let the WM manage the window
5845                         x = window.x;
5846                         y = window.y;
5847
5848                         fixed_pos = 1;
5849                         fixed_size = 1;
5850                         break;
5851 #endif
5852         }
5853 #ifdef OWN_WINDOW
5854
5855         if (own_window && !fixed_pos) {
5856                 x += workarea[0];
5857                 y += workarea[1];
5858                 text_start_x = border_margin + 1;
5859                 text_start_y = border_margin + 1;
5860                 window.x = x - border_margin - 1;
5861                 window.y = y - border_margin - 1;
5862         } else
5863 #endif
5864         {
5865                 /* If window size doesn't match to workarea's size,
5866                  * then window probably includes panels (gnome).
5867                  * Blah, doesn't work on KDE. */
5868                 if (workarea[2] != window.width || workarea[3] != window.height) {
5869                         y += workarea[1];
5870                         x += workarea[0];
5871                 }
5872
5873                 text_start_x = x;
5874                 text_start_y = y;
5875         }
5876 }
5877
5878 /* drawing stuff */
5879
5880 static int cur_x, cur_y;        /* current x and y for drawing */
5881 #endif
5882 //draw_mode also without X11 because we only need to print to stdout with FG
5883 static int draw_mode;           /* FG, BG or OUTLINE */
5884 #ifdef X11
5885 static long current_color;
5886
5887 static void text_size_updater(char *s)
5888 {
5889         int w = 0;
5890         char *p;
5891
5892         if ((output_methods & TO_X) == 0)
5893                 return;
5894         /* get string widths and skip specials */
5895         p = s;
5896         while (*p) {
5897                 if (*p == SPECIAL_CHAR) {
5898                         *p = '\0';
5899                         w += get_string_width(s);
5900                         *p = SPECIAL_CHAR;
5901
5902                         if (specials[special_index].type == BAR
5903                                         || specials[special_index].type == GAUGE
5904                                         || specials[special_index].type == GRAPH) {
5905                                 w += specials[special_index].width;
5906                                 if (specials[special_index].height > last_font_height) {
5907                                         last_font_height = specials[special_index].height;
5908                                         last_font_height += font_height();
5909                                 }
5910                         } else if (specials[special_index].type == OFFSET) {
5911                                 if (specials[special_index].arg > 0) {
5912                                         w += specials[special_index].arg;
5913                                 }
5914                         } else if (specials[special_index].type == VOFFSET) {
5915                                 last_font_height += specials[special_index].arg;
5916                         } else if (specials[special_index].type == GOTO) {
5917                                 if (specials[special_index].arg > cur_x) {
5918                                         w = (int) specials[special_index].arg;
5919                                 }
5920                         } else if (specials[special_index].type == TAB) {
5921                                 int start = specials[special_index].arg;
5922                                 int step = specials[special_index].width;
5923
5924                                 if (!step || step < 0) {
5925                                         step = 10;
5926                                 }
5927                                 w += step - (cur_x - text_start_x - start) % step;
5928                         } else if (specials[special_index].type == FONT) {
5929                                 selected_font = specials[special_index].font_added;
5930                                 if (font_height() > last_font_height) {
5931                                         last_font_height = font_height();
5932                                 }
5933                         }
5934
5935                         special_index++;
5936                         s = p + 1;
5937                 }
5938                 p++;
5939         }
5940         w += get_string_width(s);
5941         if (w > text_width) {
5942                 text_width = w;
5943         }
5944         if (text_width > maximum_width && maximum_width) {
5945                 text_width = maximum_width;
5946         }
5947
5948         text_height += last_font_height;
5949         last_font_height = font_height();
5950 }
5951
5952 static inline void set_foreground_color(long c)
5953 {
5954         if ((output_methods & TO_X) == 0)
5955                 return;
5956         current_color = c;
5957         XSetForeground(display, window.gc, c);
5958 }
5959 #endif /* X11 */
5960
5961 static void draw_string(const char *s)
5962 {
5963         int i, i2, pos, width_of_s;
5964         int max = 0;
5965         int added;
5966
5967         if (s[0] == '\0') {
5968                 return;
5969         }
5970
5971         width_of_s = get_string_width(s);
5972         if ((output_methods & TO_STDOUT) && draw_mode == FG) {
5973                 printf("%s\n", s);
5974                 fflush(stdout); /* output immediately, don't buffer */
5975         }
5976         if ((output_methods & TO_STDERR) && draw_mode == FG) {
5977                 fprintf(stderr, "%s\n", s);
5978                 fflush(stderr); /* output immediately, don't buffer */
5979         }
5980         if ((output_methods & OVERWRITE_FILE) && draw_mode == FG && overwrite_fpointer) {
5981                 fprintf(overwrite_fpointer, "%s\n", s);
5982         }
5983         if ((output_methods & APPEND_FILE) && draw_mode == FG && append_fpointer) {
5984                 fprintf(append_fpointer, "%s\n", s);
5985         }
5986         memset(tmpstring1, 0, text_buffer_size);
5987         memset(tmpstring2, 0, text_buffer_size);
5988         strncpy(tmpstring1, s, text_buffer_size - 1);
5989         pos = 0;
5990         added = 0;
5991
5992 #ifdef X11
5993         if (output_methods & TO_X) {
5994                 max = ((text_width - width_of_s) / get_string_width(" "));
5995         }
5996 #endif /* X11 */
5997         /* This code looks for tabs in the text and coverts them to spaces.
5998          * The trick is getting the correct number of spaces, and not going
5999          * over the window's size without forcing the window larger. */
6000         for (i = 0; i < (int) text_buffer_size; i++) {
6001                 if (tmpstring1[i] == '\t') {
6002                         i2 = 0;
6003                         for (i2 = 0; i2 < (8 - (1 + pos) % 8) && added <= max; i2++) {
6004                                 /* guard against overrun */
6005                                 tmpstring2[MIN(pos + i2, (int)text_buffer_size - 1)] = ' ';
6006                                 added++;
6007                         }
6008                         pos += i2;
6009                 } else {
6010                         /* guard against overrun */
6011                         tmpstring2[MIN(pos, (int) text_buffer_size - 1)] = tmpstring1[i];
6012                         pos++;
6013                 }
6014         }
6015 #ifdef X11
6016         if (output_methods & TO_X) {
6017                 if (text_width == maximum_width) {
6018                         /* this means the text is probably pushing the limit,
6019                          * so we'll chop it */
6020                         while (cur_x + get_string_width(tmpstring2) - text_start_x
6021                                         > maximum_width && strlen(tmpstring2) > 0) {
6022                                 tmpstring2[strlen(tmpstring2) - 1] = '\0';
6023                         }
6024                 }
6025         }
6026 #endif /* X11 */
6027         s = tmpstring2;
6028 #ifdef X11
6029         if (output_methods & TO_X) {
6030 #ifdef XFT
6031                 if (use_xft) {
6032                         XColor c;
6033                         XftColor c2;
6034
6035                         c.pixel = current_color;
6036                         XQueryColor(display, DefaultColormap(display, screen), &c);
6037
6038                         c2.pixel = c.pixel;
6039                         c2.color.red = c.red;
6040                         c2.color.green = c.green;
6041                         c2.color.blue = c.blue;
6042                         c2.color.alpha = fonts[selected_font].font_alpha;
6043                         if (utf8_mode) {
6044                                 XftDrawStringUtf8(window.xftdraw, &c2, fonts[selected_font].xftfont,
6045                                         cur_x, cur_y, (const XftChar8 *) s, strlen(s));
6046                         } else {
6047                                 XftDrawString8(window.xftdraw, &c2, fonts[selected_font].xftfont,
6048                                         cur_x, cur_y, (const XftChar8 *) s, strlen(s));
6049                         }
6050                 } else
6051 #endif
6052                 {
6053                         XDrawString(display, window.drawable, window.gc, cur_x, cur_y, s,
6054                                 strlen(s));
6055                 }
6056                 cur_x += width_of_s;
6057         }
6058 #endif /* X11 */
6059         memcpy(tmpstring1, s, text_buffer_size);
6060 }
6061
6062 static void draw_line(char *s)
6063 {
6064 #ifdef X11
6065         char *p;
6066         int cur_y_add = 0;
6067         int font_h;
6068         char *tmp_str;
6069
6070         if ((output_methods & TO_X) == 0) {
6071 #endif /* X11 */
6072                 draw_string(s);
6073                 return;
6074 #ifdef X11
6075         }
6076         cur_x = text_start_x;
6077         cur_y += font_ascent();
6078         font_h = font_height();
6079
6080         /* find specials and draw stuff */
6081         p = s;
6082         while (*p) {
6083                 if (*p == SPECIAL_CHAR) {
6084                         int w = 0;
6085
6086                         /* draw string before special */
6087                         *p = '\0';
6088                         draw_string(s);
6089                         *p = SPECIAL_CHAR;
6090                         s = p + 1;
6091
6092                         /* draw special */
6093                         switch (specials[special_index].type) {
6094                                 case HORIZONTAL_LINE:
6095                                 {
6096                                         int h = specials[special_index].height;
6097                                         int mid = font_ascent() / 2;
6098
6099                                         w = text_start_x + text_width - cur_x;
6100
6101                                         XSetLineAttributes(display, window.gc, h, LineSolid,
6102                                                 CapButt, JoinMiter);
6103                                         XDrawLine(display, window.drawable, window.gc, cur_x,
6104                                                 cur_y - mid / 2, cur_x + w, cur_y - mid / 2);
6105                                         break;
6106                                 }
6107
6108                                 case STIPPLED_HR:
6109                                 {
6110                                         int h = specials[special_index].height;
6111                                         int tmp_s = specials[special_index].arg;
6112                                         int mid = font_ascent() / 2;
6113                                         char ss[2] = { tmp_s, tmp_s };
6114
6115                                         w = text_start_x + text_width - cur_x - 1;
6116                                         XSetLineAttributes(display, window.gc, h, LineOnOffDash,
6117                                                 CapButt, JoinMiter);
6118                                         XSetDashes(display, window.gc, 0, ss, 2);
6119                                         XDrawLine(display, window.drawable, window.gc, cur_x,
6120                                                 cur_y - mid / 2, cur_x + w, cur_y - mid / 2);
6121                                         break;
6122                                 }
6123
6124                                 case BAR:
6125                                 {
6126                                         int h, bar_usage, by;
6127                                         if (cur_x - text_start_x > maximum_width
6128                                                         && maximum_width > 0) {
6129                                                 break;
6130                                         }
6131                                         h = specials[special_index].height;
6132                                         bar_usage = specials[special_index].arg;
6133                                         by = cur_y - (font_ascent() / 2) - 1;
6134
6135                                         if (h < font_h) {
6136                                                 by -= h / 2 - 1;
6137                                         }
6138                                         w = specials[special_index].width;
6139                                         if (w == 0) {
6140                                                 w = text_start_x + text_width - cur_x - 1;
6141                                         }
6142                                         if (w < 0) {
6143                                                 w = 0;
6144                                         }
6145
6146                                         XSetLineAttributes(display, window.gc, 1, LineSolid,
6147                                                 CapButt, JoinMiter);
6148
6149                                         XDrawRectangle(display, window.drawable, window.gc, cur_x,
6150                                                 by, w, h);
6151                                         XFillRectangle(display, window.drawable, window.gc, cur_x,
6152                                                 by, w * bar_usage / 255, h);
6153                                         if (h > cur_y_add
6154                                                         && h > font_h) {
6155                                                 cur_y_add = h;
6156                                         }
6157                                         break;
6158                                 }
6159
6160                                 case GAUGE: /* new GAUGE  */
6161                                 {
6162                                         int h, by = 0;
6163                                         unsigned long last_colour = current_color;
6164                                         float angle, px, py;
6165                                         int usage;
6166
6167                                         if (cur_x - text_start_x > maximum_width
6168                                                         && maximum_width > 0) {
6169                                                 break;
6170                                         }
6171
6172                                         h = specials[special_index].height;
6173                                         by = cur_y - (font_ascent() / 2) - 1;
6174
6175                                         if (h < font_h) {
6176                                                 by -= h / 2 - 1;
6177                                         }
6178                                         w = specials[special_index].width;
6179                                         if (w == 0) {
6180                                                 w = text_start_x + text_width - cur_x - 1;
6181                                         }
6182                                         if (w < 0) {
6183                                                 w = 0;
6184                                         }
6185
6186                                         XSetLineAttributes(display, window.gc, 1, LineSolid,
6187                                                         CapButt, JoinMiter);
6188
6189                                         XDrawArc(display, window.drawable, window.gc,
6190                                                         cur_x, by, w, h * 2, 0, 180*64);
6191
6192 #ifdef MATH
6193                                         usage = specials[special_index].arg;
6194                                         angle = (M_PI)*(float)(usage)/255.;
6195                                         px = (float)(cur_x+(w/2.))-(float)(w/2.)*cos(angle);
6196                                         py = (float)(by+(h))-(float)(h)*sin(angle);
6197
6198                                         XDrawLine(display, window.drawable, window.gc,
6199                                                         cur_x + (w/2.), by+(h), (int)(px), (int)(py));
6200 #endif /* MATH */
6201
6202                                         if (h > cur_y_add
6203                                                         && h > font_h) {
6204                                                 cur_y_add = h;
6205                                         }
6206
6207                                         set_foreground_color(last_colour);
6208
6209                                         break;
6210
6211                                 }
6212
6213                                 case GRAPH:
6214                                 {
6215                                         int h, by, i = 0, j = 0;
6216                                         int colour_idx = 0;
6217                                         unsigned long last_colour = current_color;
6218                                         unsigned long *tmpcolour = 0;
6219                                         if (cur_x - text_start_x > maximum_width
6220                                                         && maximum_width > 0) {
6221                                                 break;
6222                                         }
6223                                         h = specials[special_index].height;
6224                                         by = cur_y - (font_ascent() / 2) - 1;
6225
6226                                         if (h < font_h) {
6227                                                 by -= h / 2 - 1;
6228                                         }
6229                                         w = specials[special_index].width;
6230                                         if (w == 0) {
6231                                                 w = text_start_x + text_width - cur_x - 1;
6232                                         }
6233                                         if (w < 0) {
6234                                                 w = 0;
6235                                         }
6236                                         if (draw_graph_borders) {
6237                                                 XSetLineAttributes(display, window.gc, 1, LineSolid,
6238                                                         CapButt, JoinMiter);
6239                                                 XDrawRectangle(display, window.drawable, window.gc,
6240                                                         cur_x, by, w, h);
6241                                         }
6242                                         XSetLineAttributes(display, window.gc, 1, LineSolid,
6243                                                 CapButt, JoinMiter);
6244
6245                                         if (specials[special_index].last_colour != 0
6246                                                         || specials[special_index].first_colour != 0) {
6247                                                 tmpcolour = do_gradient(w - 1, specials[special_index].last_colour, specials[special_index].first_colour);
6248                                         }
6249                                         colour_idx = 0;
6250                                         for (i = w - 2; i > -1; i--) {
6251                                                 if (specials[special_index].last_colour != 0
6252                                                                 || specials[special_index].first_colour != 0) {
6253                                                         if (specials[special_index].tempgrad) {
6254 #ifdef DEBUG_lol
6255                                                                 assert(
6256                                                                                 (int)((float)(w - 2) - specials[special_index].graph[j] *
6257                                                                                         (w - 2) / (float)specials[special_index].graph_scale)
6258                                                                                 < w - 1
6259                                                                           );
6260                                                                 assert(
6261                                                                                 (int)((float)(w - 2) - specials[special_index].graph[j] *
6262                                                                                         (w - 2) / (float)specials[special_index].graph_scale)
6263                                                                                 > -1
6264                                                                           );
6265                                                                 if (specials[special_index].graph[j] == specials[special_index].graph_scale) {
6266                                                                         assert(
6267                                                                                         (int)((float)(w - 2) - specials[special_index].graph[j] *
6268                                                                                                 (w - 2) / (float)specials[special_index].graph_scale)
6269                                                                                         == 0
6270                                                                                   );
6271                                                                 }
6272 #endif /* DEBUG_lol */
6273                                                                 XSetForeground(display, window.gc, tmpcolour[
6274                                                                                 (int)((float)(w - 2) - specials[special_index].graph[j] *
6275                                                                                         (w - 2) / (float)specials[special_index].graph_scale)
6276                                                                                 ]);
6277                                                         } else {
6278                                                                 XSetForeground(display, window.gc, tmpcolour[colour_idx++]);
6279                                                         }
6280                                                 }
6281                                                 /* this is mugfugly, but it works */
6282                                                 XDrawLine(display, window.drawable, window.gc,
6283                                                                 cur_x + i + 1, by + h, cur_x + i + 1,
6284                                                                 round_to_int((double)by + h - specials[special_index].graph[j] *
6285                                                                         (h - 1) / specials[special_index].graph_scale));
6286                                                 if ((w - i) / ((float) (w - 2) /
6287                                                                         (specials[special_index].graph_width)) > j
6288                                                                 && j < MAX_GRAPH_DEPTH - 3) {
6289                                                         j++;
6290                                                 }
6291                                         }
6292                                         if (tmpcolour) free(tmpcolour);
6293                                         if (h > cur_y_add
6294                                                         && h > font_h) {
6295                                                 cur_y_add = h;
6296                                         }
6297                                         /* if (draw_mode == BG) {
6298                                                 set_foreground_color(default_bg_color);
6299                                         } else if (draw_mode == OUTLINE) {
6300                                                 set_foreground_color(default_out_color);
6301                                         } else {
6302                                                 set_foreground_color(default_fg_color);
6303                                         } */
6304                                         if (show_graph_range) {
6305                                                 int tmp_x = cur_x;
6306                                                 int tmp_y = cur_y;
6307                                                 unsigned short int seconds = update_interval * w;
6308                                                 char *tmp_day_str;
6309                                                 char *tmp_hour_str;
6310                                                 char *tmp_min_str;
6311                                                 char *tmp_sec_str;
6312                                                 unsigned short int timeunits;
6313                                                 if (seconds != 0) {
6314                                                         timeunits = seconds / 86400; seconds %= 86400;
6315                                                         if (timeunits > 0) {
6316                                                                 asprintf(&tmp_day_str, "%dd", timeunits);
6317                                                         } else {
6318                                                                 tmp_day_str = strdup("");
6319                                                         }
6320                                                         timeunits = seconds / 3600; seconds %= 3600;
6321                                                         if (timeunits > 0) {
6322                                                                 asprintf(&tmp_hour_str, "%dh", timeunits);
6323                                                         } else {
6324                                                                 tmp_hour_str = strdup("");
6325                                                         }
6326                                                         timeunits = seconds / 60; seconds %= 60;
6327                                                         if (timeunits > 0) {
6328                                                                 asprintf(&tmp_min_str, "%dm", timeunits);
6329                                                         } else {
6330                                                                 tmp_min_str = strdup("");
6331                                                         }
6332                                                         if (seconds > 0) {
6333                                                                 asprintf(&tmp_sec_str, "%ds", seconds);
6334                                                         } else {
6335                                                                 tmp_sec_str = strdup("");
6336                                                         }
6337                                                         asprintf(&tmp_str, "%s%s%s%s", tmp_day_str, tmp_hour_str, tmp_min_str, tmp_sec_str);
6338                                                         free(tmp_day_str); free(tmp_hour_str); free(tmp_min_str); free(tmp_sec_str);
6339                                                 } else {
6340                                                         asprintf(&tmp_str, "Range not possible"); // should never happen, but better safe then sorry
6341                                                 }
6342                                                 cur_x += (w / 2) - (font_ascent() * (strlen(tmp_str) / 2));
6343                                                 cur_y += font_h / 2;
6344                                                 draw_string(tmp_str);
6345                                                 free(tmp_str);
6346                                                 cur_x = tmp_x;
6347                                                 cur_y = tmp_y;
6348                                         }
6349 #ifdef MATH
6350                                         if (show_graph_scale && (specials[special_index].show_scale == 1)) {
6351                                                 int tmp_x = cur_x;
6352                                                 int tmp_y = cur_y;
6353                                                 cur_x += font_ascent() / 2;
6354                                                 cur_y += font_h / 2;
6355                                                 tmp_str = (char *)
6356                                                         calloc(log10(floor(specials[special_index].graph_scale)) + 4,
6357                                                                         sizeof(char));
6358                                                 sprintf(tmp_str, "%.1f", specials[special_index].graph_scale);
6359                                                 draw_string(tmp_str);
6360                                                 free(tmp_str);
6361                                                 cur_x = tmp_x;
6362                                                 cur_y = tmp_y;
6363                                         }
6364 #endif
6365                                         set_foreground_color(last_colour);
6366                                         break;
6367                                 }
6368
6369                                 case FONT:
6370                                 {
6371                                         int old = font_ascent();
6372
6373                                         cur_y -= font_ascent();
6374                                         selected_font = specials[special_index].font_added;
6375                                         if (cur_y + font_ascent() < cur_y + old) {
6376                                                 cur_y += old;
6377                                         } else {
6378                                                 cur_y += font_ascent();
6379                                         }
6380                                         set_font();
6381                                         font_h = font_height();
6382                                         break;
6383                                 }
6384                                 case FG:
6385                                         if (draw_mode == FG) {
6386                                                 set_foreground_color(specials[special_index].arg);
6387                                         }
6388                                         break;
6389
6390                                 case BG:
6391                                         if (draw_mode == BG) {
6392                                                 set_foreground_color(specials[special_index].arg);
6393                                         }
6394                                         break;
6395
6396                                 case OUTLINE:
6397                                         if (draw_mode == OUTLINE) {
6398                                                 set_foreground_color(specials[special_index].arg);
6399                                         }
6400                                         break;
6401
6402                                 case OFFSET:
6403                                         w += specials[special_index].arg;
6404                                         break;
6405
6406                                 case VOFFSET:
6407                                         cur_y += specials[special_index].arg;
6408                                         break;
6409
6410                                 case GOTO:
6411                                         if (specials[special_index].arg >= 0) {
6412                                                 cur_x = (int) specials[special_index].arg;
6413                                         }
6414                                         break;
6415
6416                                 case TAB:
6417                                 {
6418                                         int start = specials[special_index].arg;
6419                                         int step = specials[special_index].width;
6420
6421                                         if (!step || step < 0) {
6422                                                 step = 10;
6423                                         }
6424                                         w = step - (cur_x - text_start_x - start) % step;
6425                                         break;
6426                                 }
6427
6428                                 case ALIGNR:
6429                                 {
6430                                         /* TODO: add back in "+ border_margin" to the end of
6431                                          * this line? */
6432                                         int pos_x = text_start_x + text_width -
6433                                                 get_string_width_special(s);
6434
6435                                         /* printf("pos_x %i text_start_x %i text_width %i cur_x %i "
6436                                                 "get_string_width(p) %i gap_x %i "
6437                                                 "specials[special_index].arg %i border_margin %i "
6438                                                 "border_width %i\n", pos_x, text_start_x, text_width,
6439                                                 cur_x, get_string_width_special(s), gap_x,
6440                                                 specials[special_index].arg, border_margin,
6441                                                 border_width); */
6442                                         if (pos_x > specials[special_index].arg && pos_x > cur_x) {
6443                                                 cur_x = pos_x - specials[special_index].arg;
6444                                         }
6445                                         break;
6446                                 }
6447
6448                                 case ALIGNC:
6449                                 {
6450                                         int pos_x = (text_width) / 2 - get_string_width_special(s) /
6451                                                 2 - (cur_x - text_start_x);
6452                                         /* int pos_x = text_start_x + text_width / 2 -
6453                                                 get_string_width_special(s) / 2; */
6454
6455                                         /* printf("pos_x %i text_start_x %i text_width %i cur_x %i "
6456                                                 "get_string_width(p) %i gap_x %i "
6457                                                 "specials[special_index].arg %i\n", pos_x, text_start_x,
6458                                                 text_width, cur_x, get_string_width(s), gap_x,
6459                                                 specials[special_index].arg); */
6460                                         if (pos_x > specials[special_index].arg) {
6461                                                 w = pos_x - specials[special_index].arg;
6462                                         }
6463                                         break;
6464                                 }
6465                         }
6466
6467                         cur_x += w;
6468
6469                         special_index++;
6470                 }
6471
6472                 p++;
6473         }
6474
6475         if (cur_y_add > 0) {
6476                 cur_y += cur_y_add;
6477         }
6478         draw_string(s);
6479         cur_y += font_descent();
6480
6481 #endif /* X11 */
6482 }
6483
6484 static void draw_text(void)
6485 {
6486 #ifdef X11
6487         if (output_methods & TO_X) {
6488                 cur_y = text_start_y;
6489
6490                 /* draw borders */
6491                 if (draw_borders && border_width > 0) {
6492                         unsigned int b = (border_width + 1) / 2;
6493
6494                         if (stippled_borders) {
6495                                 char ss[2] = { stippled_borders, stippled_borders };
6496                                 XSetLineAttributes(display, window.gc, border_width, LineOnOffDash,
6497                                         CapButt, JoinMiter);
6498                                 XSetDashes(display, window.gc, 0, ss, 2);
6499                         } else {
6500                                 XSetLineAttributes(display, window.gc, border_width, LineSolid,
6501                                         CapButt, JoinMiter);
6502                         }
6503
6504                         XDrawRectangle(display, window.drawable, window.gc,
6505                                 text_start_x - border_margin + b, text_start_y - border_margin + b,
6506                                 text_width + border_margin * 2 - 1 - b * 2,
6507                                 text_height + border_margin * 2 - 1 - b * 2);
6508                 }
6509
6510                 /* draw text */
6511                 special_index = 0;
6512         }
6513 #endif /* X11 */
6514         for_each_line(text_buffer, draw_line);
6515 }
6516
6517 static void draw_stuff(void)
6518 {
6519         if (overwrite_file) {
6520                 overwrite_fpointer = fopen(overwrite_file, "w");
6521                 if(!overwrite_fpointer)
6522                         ERR("Can't overwrite '%s' anymore", overwrite_file);
6523         }
6524         if (append_file) {
6525                 append_fpointer = fopen(append_file, "a");
6526                 if(!append_fpointer)
6527                         ERR("Can't append '%s' anymore", append_file);
6528         }
6529 #ifdef X11
6530         if (output_methods & TO_X) {
6531                 selected_font = 0;
6532                 if (draw_shades && !draw_outline) {
6533                         text_start_x++;
6534                         text_start_y++;
6535                         set_foreground_color(default_bg_color);
6536                         draw_mode = BG;
6537                         draw_text();
6538                         text_start_x--;
6539                         text_start_y--;
6540                 }
6541
6542                 if (draw_outline) {
6543                         int i, j;
6544                         selected_font = 0;
6545
6546                         for (i = -1; i < 2; i++) {
6547                                 for (j = -1; j < 2; j++) {
6548                                         if (i == 0 && j == 0) {
6549                                                 continue;
6550                                         }
6551                                         text_start_x += i;
6552                                         text_start_y += j;
6553                                         set_foreground_color(default_out_color);
6554                                         draw_mode = OUTLINE;
6555                                         draw_text();
6556                                         text_start_x -= i;
6557                                         text_start_y -= j;
6558                                 }
6559                         }
6560                 }
6561
6562                 set_foreground_color(default_fg_color);
6563         }
6564 #endif /* X11 */
6565         draw_mode = FG;
6566         draw_text();
6567 #ifdef X11
6568         if (output_methods & TO_X) {
6569 #ifdef HAVE_XDBE
6570                 if (use_xdbe) {
6571                         XdbeSwapInfo swap;
6572
6573                         swap.swap_window = window.window;
6574                         swap.swap_action = XdbeBackground;
6575                         XdbeSwapBuffers(display, &swap, 1);
6576                 }
6577 #endif
6578         }
6579 #endif /* X11 */
6580         if(overwrite_fpointer) {
6581                 fclose(overwrite_fpointer);
6582                 overwrite_fpointer = 0;
6583         }
6584         if(append_fpointer) {
6585                 fclose(append_fpointer);
6586                 append_fpointer = 0;
6587         }
6588 }
6589
6590 #ifdef X11
6591 static void clear_text(int exposures)
6592 {
6593 #ifdef HAVE_XDBE
6594         if (use_xdbe) {
6595                 /* The swap action is XdbeBackground, which clears */
6596                 return;
6597         } else
6598 #endif
6599         if (display && window.window) { // make sure these are !null
6600                 /* there is some extra space for borders and outlines */
6601                 XClearArea(display, window.window, text_start_x - border_margin - 1,
6602                         text_start_y - border_margin - 1,
6603                         text_width + border_margin * 2 + 2,
6604                         text_height + border_margin * 2 + 2, exposures ? True : 0);
6605         }
6606 }
6607 #endif /* X11 */
6608
6609 static int need_to_update;
6610
6611 /* update_text() generates new text and clears old text area */
6612 static void update_text(void)
6613 {
6614 #ifdef IMLIB2
6615         cimlib_cleanup();
6616 #endif /* IMLIB2 */
6617         generate_text();
6618 #ifdef X11
6619         if (output_methods & TO_X)
6620                 clear_text(1);
6621 #endif /* X11 */
6622         need_to_update = 1;
6623 }
6624
6625 #ifdef HAVE_SYS_INOTIFY_H
6626 int inotify_fd;
6627 #endif
6628
6629 static void main_loop(void)
6630 {
6631         int terminate = 0;
6632 #ifdef SIGNAL_BLOCKING
6633         sigset_t newmask, oldmask;
6634 #endif
6635         double t;
6636 #ifdef HAVE_SYS_INOTIFY_H
6637         int inotify_config_wd = 0;
6638 #define INOTIFY_EVENT_SIZE  (sizeof(struct inotify_event))
6639 #define INOTIFY_BUF_LEN     (20 * (INOTIFY_EVENT_SIZE + 16))
6640         char inotify_buff[INOTIFY_BUF_LEN];
6641 #endif /* HAVE_SYS_INOTIFY_H */
6642
6643
6644 #ifdef SIGNAL_BLOCKING
6645         sigemptyset(&newmask);
6646         sigaddset(&newmask, SIGINT);
6647         sigaddset(&newmask, SIGTERM);
6648         sigaddset(&newmask, SIGUSR1);
6649 #endif
6650
6651         next_update_time = last_update_time = get_time();
6652         info.looped = 0;
6653         while (terminate == 0 && (total_run_times == 0 || info.looped < total_run_times)) {
6654                 info.looped++;
6655
6656 #ifdef SIGNAL_BLOCKING
6657                 /* block signals.  we will inspect for pending signals later */
6658                 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
6659                         CRIT_ERR("unable to sigprocmask()");
6660                 }
6661 #endif
6662
6663 #ifdef X11
6664                 if (output_methods & TO_X) {
6665                         XFlush(display);
6666
6667                         /* wait for X event or timeout */
6668
6669                         if (!XPending(display)) {
6670                                 fd_set fdsr;
6671                                 struct timeval tv;
6672                                 int s;
6673                                 t = next_update_time - get_time();
6674
6675                                 if (t < 0) {
6676                                         t = 0;
6677                                 } else if (t > update_interval) {
6678                                         t = update_interval;
6679                                 }
6680
6681                                 tv.tv_sec = (long) t;
6682                                 tv.tv_usec = (long) (t * 1000000) % 1000000;
6683                                 FD_ZERO(&fdsr);
6684                                 FD_SET(ConnectionNumber(display), &fdsr);
6685
6686                                 s = select(ConnectionNumber(display) + 1, &fdsr, 0, 0, &tv);
6687                                 if (s == -1) {
6688                                         if (errno != EINTR) {
6689                                                 ERR("can't select(): %s", strerror(errno));
6690                                         }
6691                                 } else {
6692                                         /* timeout */
6693                                         if (s == 0) {
6694                                                 update_text();
6695                                         }
6696                                 }
6697                         }
6698
6699                         if (need_to_update) {
6700 #ifdef OWN_WINDOW
6701                                 int wx = window.x, wy = window.y;
6702 #endif
6703
6704                                 need_to_update = 0;
6705                                 selected_font = 0;
6706                                 update_text_area();
6707 #ifdef OWN_WINDOW
6708                                 if (own_window) {
6709                                         /* resize window if it isn't right size */
6710                                         if (!fixed_size
6711                                                 && (text_width + border_margin * 2 + 1 != window.width
6712                                                 || text_height + border_margin * 2 + 1 != window.height)) {
6713                                                         window.width = text_width + border_margin * 2 + 1;
6714                                                         window.height = text_height + border_margin * 2 + 1;
6715                                                         XResizeWindow(display, window.window, window.width,
6716                                                                 window.height);
6717                                                         if (own_window) {
6718                                                                 set_transparent_background(window.window);
6719                                                         }
6720                                         }
6721
6722                                         /* move window if it isn't in right position */
6723                                         if (!fixed_pos && (window.x != wx || window.y != wy)) {
6724                                                 XMoveWindow(display, window.window, window.x, window.y);
6725                                         }
6726                                 }
6727 #endif
6728
6729                                 clear_text(1);
6730
6731 #ifdef HAVE_XDBE
6732                                 if (use_xdbe) {
6733                                         XRectangle r;
6734
6735                                         r.x = text_start_x - border_margin;
6736                                         r.y = text_start_y - border_margin;
6737                                         r.width = text_width + border_margin * 2;
6738                                         r.height = text_height + border_margin * 2;
6739                                         XUnionRectWithRegion(&r, x11_stuff.region, x11_stuff.region);
6740                                 }
6741 #endif
6742                         }
6743
6744                         /* handle X events */
6745                         while (XPending(display)) {
6746                                 XEvent ev;
6747
6748                                 XNextEvent(display, &ev);
6749                                 switch (ev.type) {
6750                                         case Expose:
6751                                         {
6752                                                 XRectangle r;
6753                                                 r.x = ev.xexpose.x;
6754                                                 r.y = ev.xexpose.y;
6755                                                 r.width = ev.xexpose.width;
6756                                                 r.height = ev.xexpose.height;
6757                                                 XUnionRectWithRegion(&r, x11_stuff.region, x11_stuff.region);
6758                                                 break;
6759                                         }
6760
6761 #ifdef OWN_WINDOW
6762                                         case ReparentNotify:
6763                                                 /* set background to ParentRelative for all parents */
6764                                                 if (own_window) {
6765                                                         set_transparent_background(window.window);
6766                                                 }
6767                                                 break;
6768
6769                                         case ConfigureNotify:
6770                                                 if (own_window) {
6771                                                         /* if window size isn't what expected, set fixed size */
6772                                                         if (ev.xconfigure.width != window.width
6773                                                                         || ev.xconfigure.height != window.height) {
6774                                                                 if (window.width != 0 && window.height != 0) {
6775                                                                         fixed_size = 1;
6776                                                                 }
6777
6778                                                                 /* clear old stuff before screwing up
6779                                                                  * size and pos */
6780                                                                 clear_text(1);
6781
6782                                                                 {
6783                                                                         XWindowAttributes attrs;
6784                                                                         if (XGetWindowAttributes(display,
6785                                                                                         window.window, &attrs)) {
6786                                                                                 window.width = attrs.width;
6787                                                                                 window.height = attrs.height;
6788                                                                         }
6789                                                                 }
6790
6791                                                                 text_width = window.width - border_margin * 2 - 1;
6792                                                                 text_height = window.height - border_margin * 2 - 1;
6793                                                                 if (text_width > maximum_width
6794                                                                                 && maximum_width > 0) {
6795                                                                         text_width = maximum_width;
6796                                                                 }
6797                                                         }
6798
6799                                                         /* if position isn't what expected, set fixed pos
6800                                                          * total_updates avoids setting fixed_pos when window
6801                                                          * is set to weird locations when started */
6802                                                         /* // this is broken
6803                                                         if (total_updates >= 2 && !fixed_pos
6804                                                                         && (window.x != ev.xconfigure.x
6805                                                                         || window.y != ev.xconfigure.y)
6806                                                                         && (ev.xconfigure.x != 0
6807                                                                         || ev.xconfigure.y != 0)) {
6808                                                                 fixed_pos = 1;
6809                                                         } */
6810                                                         set_font();
6811                                                 }
6812                                                 break;
6813
6814                                         case ButtonPress:
6815                                                 if (own_window) {
6816                                                         /* if an ordinary window with decorations */
6817                                                         if ((window.type == TYPE_NORMAL)
6818                                                                 && (!TEST_HINT(window.hints,
6819                                                                 HINT_UNDECORATED))) {
6820                                                                 /* allow conky to hold input focus. */
6821                                                                 break;
6822                                                         } else {
6823                                                                 /* forward the click to the desktop window */
6824                                                                 XUngrabPointer(display, ev.xbutton.time);
6825                                                                 ev.xbutton.window = window.desktop;
6826                                                                 ev.xbutton.x = ev.xbutton.x_root;
6827                                                                 ev.xbutton.y = ev.xbutton.y_root;
6828                                                                 XSendEvent(display, ev.xbutton.window, False,
6829                                                                         ButtonPressMask, &ev);
6830                                                                 XSetInputFocus(display, ev.xbutton.window,
6831                                                                         RevertToParent, ev.xbutton.time);
6832                                                         }
6833                                                 }
6834                                                 break;
6835
6836                                         case ButtonRelease:
6837                                                 if (own_window) {
6838                                                         /* if an ordinary window with decorations */
6839                                                         if ((window.type == TYPE_NORMAL)
6840                                                                         && (!TEST_HINT(window.hints,
6841                                                                         HINT_UNDECORATED))) {
6842                                                                 /* allow conky to hold input focus. */
6843                                                                 break;
6844                                                         } else {
6845                                                                 /* forward the release to the desktop window */
6846                                                                 ev.xbutton.window = window.desktop;
6847                                                                 ev.xbutton.x = ev.xbutton.x_root;
6848                                                                 ev.xbutton.y = ev.xbutton.y_root;
6849                                                                 XSendEvent(display, ev.xbutton.window, False,
6850                                                                         ButtonReleaseMask, &ev);
6851                                                         }
6852                                                 }
6853                                                 break;
6854
6855 #endif
6856
6857                                         default:
6858 #ifdef HAVE_XDAMAGE
6859                                                 if (ev.type == x11_stuff.event_base + XDamageNotify) {
6860                                                         XDamageNotifyEvent *dev = (XDamageNotifyEvent *) &ev;
6861
6862                                                         XFixesSetRegion(display, x11_stuff.part, &dev->area, 1);
6863                                                         XFixesUnionRegion(display, x11_stuff.region2, x11_stuff.region2, x11_stuff.part);
6864                                                 }
6865 #endif /* HAVE_XDAMAGE */
6866                                                 break;
6867                                 }
6868                         }
6869
6870 #ifdef HAVE_XDAMAGE
6871                         XDamageSubtract(display, x11_stuff.damage, x11_stuff.region2, None);
6872                         XFixesSetRegion(display, x11_stuff.region2, 0, 0);
6873 #endif /* HAVE_XDAMAGE */
6874
6875                         /* XDBE doesn't seem to provide a way to clear the back buffer without
6876                          * interfering with the front buffer, other than passing XdbeBackground
6877                          * to XdbeSwapBuffers. That means that if we're using XDBE, we need to
6878                          * redraw the text even if it wasn't part of the exposed area. OTOH,
6879                          * if we're not going to call draw_stuff at all, then no swap happens
6880                          * and we can safely do nothing. */
6881
6882                         if (!XEmptyRegion(x11_stuff.region)) {
6883 #ifdef HAVE_XDBE
6884                                 if (use_xdbe) {
6885                                         XRectangle r;
6886
6887                                         r.x = text_start_x - border_margin;
6888                                         r.y = text_start_y - border_margin;
6889                                         r.width = text_width + border_margin * 2;
6890                                         r.height = text_height + border_margin * 2;
6891                                         XUnionRectWithRegion(&r, x11_stuff.region, x11_stuff.region);
6892                                 }
6893 #endif
6894                                 XSetRegion(display, window.gc, x11_stuff.region);
6895 #ifdef XFT
6896                                 if (use_xft) {
6897                                         XftDrawSetClip(window.xftdraw, x11_stuff.region);
6898                                 }
6899 #endif
6900 #ifdef IMLIB2
6901                                 cimlib_render(text_start_x, text_start_y, window.width, window.height);
6902 #endif /* IMLIB2 */
6903                                 draw_stuff();
6904                                 XDestroyRegion(x11_stuff.region);
6905                                 x11_stuff.region = XCreateRegion();
6906                         }
6907                 } else {
6908 #endif /* X11 */
6909                         t = (next_update_time - get_time()) * 1000000;
6910                         if(t > 0) usleep((useconds_t)t);
6911                         update_text();
6912                         draw_stuff();
6913 #ifdef X11
6914                 }
6915 #endif /* X11 */
6916
6917 #ifdef SIGNAL_BLOCKING
6918                 /* unblock signals of interest and let handler fly */
6919                 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
6920                         CRIT_ERR("unable to sigprocmask()");
6921                 }
6922 #endif
6923
6924                 switch (g_signal_pending) {
6925                         case SIGHUP:
6926                         case SIGUSR1:
6927                                 ERR("received SIGHUP or SIGUSR1. reloading the config file.");
6928                                 reload_config();
6929                                 break;
6930                         case SIGINT:
6931                         case SIGTERM:
6932                                 ERR("received SIGINT or SIGTERM to terminate. bye!");
6933                                 terminate = 1;
6934                                 clean_up();
6935 #ifdef X11
6936                                 if (output_methods & TO_X) {
6937                                         XDestroyRegion(x11_stuff.region);
6938                                         x11_stuff.region = NULL;
6939 #ifdef HAVE_XDAMAGE
6940                                         XDamageDestroy(display, x11_stuff.damage);
6941                                         XFixesDestroyRegion(display, x11_stuff.region2);
6942                                         XFixesDestroyRegion(display, x11_stuff.part);
6943 #endif /* HAVE_XDAMAGE */
6944                                         if (disp) {
6945                                                 free(disp);
6946                                         }
6947                                 }
6948 #endif /* X11 */
6949                                 if(overwrite_file) {
6950                                         free(overwrite_file);
6951                                         overwrite_file = 0;
6952                                 }
6953                                 if(append_file) {
6954                                         free(append_file);
6955                                         append_file = 0;
6956                                 }
6957                                 break;
6958                         default:
6959                                 /* Reaching here means someone set a signal
6960                                  * (SIGXXXX, signal_handler), but didn't write any code
6961                                  * to deal with it.
6962                                  * If you don't want to handle a signal, don't set a handler on
6963                                  * it in the first place. */
6964                                 if (g_signal_pending) {
6965                                         ERR("ignoring signal (%d)", g_signal_pending);
6966                                 }
6967                                 break;
6968                 }
6969 #ifdef HAVE_SYS_INOTIFY_H
6970                 if (inotify_fd && !inotify_config_wd) {
6971                         inotify_config_wd = inotify_add_watch(inotify_fd,
6972                                         current_config,
6973                                         IN_MODIFY);
6974                 }
6975                 if (inotify_fd && inotify_config_wd) {
6976                         int len = 0, idx = 0;
6977                         fd_set descriptors;
6978                         struct timeval time_to_wait;
6979
6980                         FD_ZERO (&descriptors);
6981                         FD_SET(inotify_fd, &descriptors);
6982
6983                         time_to_wait.tv_sec = time_to_wait.tv_usec = 0;
6984
6985                         select(inotify_fd + 1, &descriptors, NULL, NULL, &time_to_wait);
6986                         if (FD_ISSET(inotify_fd, &descriptors)) {
6987                                 /* process inotify events */
6988                                 len = read(inotify_fd, inotify_buff, INOTIFY_BUF_LEN);
6989                                 while (len > 0 && idx < len) {
6990                                         struct inotify_event *ev = (struct inotify_event *) &inotify_buff[idx];
6991                                         if (ev->wd == inotify_config_wd && (ev->mask & IN_MODIFY || ev->mask & IN_IGNORED)) {
6992                                                 /* current_config should be reloaded */
6993                                                 ERR("'%s' modified, reloading...", current_config);
6994                                                 reload_config();
6995                                                 if (ev->mask & IN_IGNORED) {
6996                                                         /* for some reason we get IN_IGNORED here
6997                                                          * sometimes, so we need to re-add the watch */
6998                                                         inotify_config_wd = inotify_add_watch(inotify_fd,
6999                                                                         current_config,
7000                                                                         IN_MODIFY);
7001                                                 }
7002                                         }
7003 #ifdef HAVE_LUA
7004                                         else {
7005                                                 llua_inotify_query(ev->wd, ev->mask);
7006                                         }
7007 #endif /* HAVE_LUA */
7008                                         idx += INOTIFY_EVENT_SIZE + ev->len;
7009                                 }
7010                         }
7011                 }
7012 #endif /* HAVE_SYS_INOTIFY_H */
7013
7014                 g_signal_pending = 0;
7015         }
7016
7017 #ifdef HAVE_SYS_INOTIFY_H
7018         if (inotify_fd) {
7019                 inotify_rm_watch(inotify_fd, inotify_config_wd);
7020                 close(inotify_fd);
7021                 inotify_fd = inotify_config_wd = 0;
7022         }
7023 #endif /* HAVE_SYS_INOTIFY_H */
7024
7025 #ifdef X11
7026         X11_destroy_window();
7027 #endif /* X11 */
7028 }
7029
7030 static void load_config_file(const char *);
7031 static void load_config_file_x11(const char *);
7032
7033         /* reload the config file */
7034 static void reload_config(void)
7035 {
7036         timed_thread_destroy_registered_threads();
7037
7038         if (info.cpu_usage) {
7039                 free(info.cpu_usage);
7040                 info.cpu_usage = NULL;
7041         }
7042
7043         if (info.mail) {
7044                 free(info.mail);
7045         }
7046
7047 #ifdef X11
7048         free_fonts();
7049 #endif /* X11 */
7050
7051 #ifdef TCP_PORT_MONITOR
7052         tcp_portmon_clear();
7053 #endif
7054
7055 #ifdef HAVE_LUA
7056         llua_close();
7057 #endif /* HAVE_LUA */
7058
7059 #ifdef X11
7060         X11_destroy_window();
7061 #endif /* X11 */
7062
7063         if (current_config) {
7064                 clear_fs_stats();
7065                 load_config_file(current_config);
7066                 load_config_file_x11(current_config);
7067
7068                 /* re-init specials array */
7069                 if ((specials = realloc((void *) specials,
7070                                 sizeof(struct special_t) * max_specials)) == 0) {
7071                         ERR("failed to realloc specials array");
7072                 }
7073
7074 #ifdef X11
7075                 x_initialised = NO;
7076                 if (output_methods & TO_X) {
7077                         X11_initialisation();
7078                 }
7079 #endif /* X11 */
7080                 extract_variable_text(global_text);
7081                 free(global_text);
7082                 global_text = NULL;
7083                 if (tmpstring1) {
7084                         free(tmpstring1);
7085                 }
7086                 tmpstring1 = malloc(text_buffer_size);
7087                 memset(tmpstring1, 0, text_buffer_size);
7088                 if (tmpstring2) {
7089                         free(tmpstring2);
7090                 }
7091                 tmpstring2 = malloc(text_buffer_size);
7092                 memset(tmpstring2, 0, text_buffer_size);
7093                 if (text_buffer) {
7094                         free(text_buffer);
7095                 }
7096                 text_buffer = malloc(max_user_text);
7097                 memset(text_buffer, 0, max_user_text);
7098                 update_text();
7099 #ifdef X11
7100                 X11_create_window();
7101 #endif /* X11 */
7102         }
7103 }
7104
7105 static void clean_up(void)
7106 {
7107         int i;
7108         timed_thread_destroy_registered_threads();
7109
7110         if (info.cpu_usage) {
7111                 free(info.cpu_usage);
7112                 info.cpu_usage = NULL;
7113         }
7114 #ifdef X11
7115         if (output_methods & TO_X) {
7116 #ifdef HAVE_XDBE
7117                 if (use_xdbe) {
7118                         XdbeDeallocateBackBufferName(display, window.back_buffer);
7119                 }
7120 #endif
7121 #ifdef OWN_WINDOW
7122                 if (own_window) {
7123                         XDestroyWindow(display, window.window);
7124                         XClearWindow(display, RootWindow(display, screen));
7125                         XFlush(display);
7126                 } else
7127 #endif
7128                 {
7129                         XClearWindow(display, RootWindow(display, screen));
7130                         clear_text(1);
7131                         XFlush(display);
7132                 }
7133
7134                 XFreeGC(display, window.gc);
7135                 free_fonts();
7136         }
7137
7138 #endif /* X11 */
7139
7140 #ifdef HAVE_OPENMP
7141 #pragma omp parallel for
7142 #endif /* HAVE_OPENMP */
7143         for (i = 0; i < MAX_TEMPLATES; i++) {
7144                 if (template[i]) {
7145                         free(template[i]);
7146                         template[i] = NULL;
7147                 }
7148         }
7149
7150         free_text_objects(&global_root_object, 0);
7151         if (tmpstring1) {
7152                 free(tmpstring1);
7153                 tmpstring1 = 0;
7154         }
7155         if (tmpstring2) {
7156                 free(tmpstring2);
7157                 tmpstring2 = 0;
7158         }
7159         if (text_buffer) {
7160                 free(text_buffer);
7161                 text_buffer = 0;
7162         }
7163
7164         if (global_text) {
7165                 free(global_text);
7166                 global_text = 0;
7167         }
7168
7169         free(current_config);
7170
7171 #ifdef TCP_PORT_MONITOR
7172         tcp_portmon_clear();
7173 #endif
7174 #ifdef RSS
7175         free_rss_info();
7176 #endif
7177 #ifdef HAVE_LUA
7178         llua_close();
7179 #endif /* HAVE_LUA */
7180
7181         if (specials) {
7182                 for (i = 0; i < special_count; i++) {
7183                         if (specials[i].type == GRAPH) {
7184                                 free(specials[i].graph);
7185                         }
7186                 }
7187                 free(specials);
7188                 specials = NULL;
7189         }
7190
7191         clear_diskio_stats();
7192 }
7193
7194 static int string_to_bool(const char *s)
7195 {
7196         if (!s) {
7197                 // Assumes an option without a true/false means true
7198                 return 1;
7199         } else if (strcasecmp(s, "yes") == EQUAL) {
7200                 return 1;
7201         } else if (strcasecmp(s, "true") == EQUAL) {
7202                 return 1;
7203         } else if (strcasecmp(s, "1") == EQUAL) {
7204                 return 1;
7205         }
7206         return 0;
7207 }
7208
7209 #ifdef X11
7210 static enum alignment string_to_alignment(const char *s)
7211 {
7212         if (strcasecmp(s, "top_left") == EQUAL) {
7213                 return TOP_LEFT;
7214         } else if (strcasecmp(s, "top_right") == EQUAL) {
7215                 return TOP_RIGHT;
7216         } else if (strcasecmp(s, "top_middle") == EQUAL) {
7217                 return TOP_MIDDLE;
7218         } else if (strcasecmp(s, "bottom_left") == EQUAL) {
7219                 return BOTTOM_LEFT;
7220         } else if (strcasecmp(s, "bottom_right") == EQUAL) {
7221                 return BOTTOM_RIGHT;
7222         } else if (strcasecmp(s, "bottom_middle") == EQUAL) {
7223                 return BOTTOM_MIDDLE;
7224         } else if (strcasecmp(s, "middle_left") == EQUAL) {
7225                 return MIDDLE_LEFT;
7226         } else if (strcasecmp(s, "middle_right") == EQUAL) {
7227                 return MIDDLE_RIGHT;
7228         } else if (strcasecmp(s, "tl") == EQUAL) {
7229                 return TOP_LEFT;
7230         } else if (strcasecmp(s, "tr") == EQUAL) {
7231                 return TOP_RIGHT;
7232         } else if (strcasecmp(s, "tm") == EQUAL) {
7233                 return TOP_MIDDLE;
7234         } else if (strcasecmp(s, "bl") == EQUAL) {
7235                 return BOTTOM_LEFT;
7236         } else if (strcasecmp(s, "br") == EQUAL) {
7237                 return BOTTOM_RIGHT;
7238         } else if (strcasecmp(s, "bm") == EQUAL) {
7239                 return BOTTOM_MIDDLE;
7240         } else if (strcasecmp(s, "ml") == EQUAL) {
7241                 return MIDDLE_LEFT;
7242         } else if (strcasecmp(s, "mr") == EQUAL) {
7243                 return MIDDLE_RIGHT;
7244         } else if (strcasecmp(s, "none") == EQUAL) {
7245                 return NONE;
7246         }
7247         return TOP_LEFT;
7248 }
7249 #endif /* X11 */
7250
7251 #ifdef X11
7252 static void set_default_configurations_for_x(void)
7253 {
7254         default_fg_color = WhitePixel(display, screen);
7255         default_bg_color = BlackPixel(display, screen);
7256         default_out_color = BlackPixel(display, screen);
7257         color0 = default_fg_color;
7258         color1 = default_fg_color;
7259         color2 = default_fg_color;
7260         color3 = default_fg_color;
7261         color4 = default_fg_color;
7262         color5 = default_fg_color;
7263         color6 = default_fg_color;
7264         color7 = default_fg_color;
7265         color8 = default_fg_color;
7266         color9 = default_fg_color;
7267 }
7268 #endif /* X11 */
7269
7270 static void set_default_configurations(void)
7271 {
7272         int i;
7273         update_uname();
7274         fork_to_background = 0;
7275         total_run_times = 0;
7276         info.cpu_avg_samples = 2;
7277         info.net_avg_samples = 2;
7278         info.diskio_avg_samples = 2;
7279         info.memmax = 0;
7280         top_cpu = 0;
7281         cpu_separate = 0;
7282         short_units = 0;
7283         top_mem = 0;
7284         top_time = 0;
7285 #ifdef MPD
7286         mpd_set_host("localhost");
7287         mpd_set_port("6600");
7288 #endif
7289 #ifdef XMMS2
7290         info.xmms2.artist = NULL;
7291         info.xmms2.album = NULL;
7292         info.xmms2.title = NULL;
7293         info.xmms2.genre = NULL;
7294         info.xmms2.comment = NULL;
7295         info.xmms2.url = NULL;
7296         info.xmms2.status = NULL;
7297         info.xmms2.playlist = NULL;
7298 #endif
7299         use_spacer = NO_SPACER;
7300 #ifdef X11
7301         output_methods = TO_X;
7302 #else
7303         output_methods = TO_STDOUT;
7304 #endif
7305 #ifdef X11
7306         show_graph_scale = 0;
7307         show_graph_range = 0;
7308         draw_shades = 1;
7309         draw_borders = 0;
7310         draw_graph_borders = 1;
7311         draw_outline = 0;
7312         set_first_font("6x10");
7313         gap_x = 5;
7314         gap_y = 60;
7315         minimum_width = 5;
7316         minimum_height = 5;
7317         maximum_width = 0;
7318 #ifdef OWN_WINDOW
7319         own_window = 0;
7320         window.type = TYPE_NORMAL;
7321         window.hints = 0;
7322         strcpy(window.class_name, PACKAGE_NAME);
7323         sprintf(window.title, PACKAGE_NAME" (%s)", info.uname_s.nodename);
7324 #endif
7325         stippled_borders = 0;
7326         border_margin = 3;
7327         border_width = 1;
7328         text_alignment = BOTTOM_LEFT;
7329         info.x11.monitor.number = 1;
7330         info.x11.monitor.current = 0;
7331 #endif /* X11 */
7332
7333 #ifdef HAVE_OPENMP
7334 #pragma omp parallel for
7335 #endif /* HAVE_OPENMP */
7336         for (i = 0; i < MAX_TEMPLATES; i++) {
7337                 template[i] = strdup("");
7338         }
7339
7340         free(current_mail_spool);
7341         {
7342                 char buf[256];
7343
7344                 variable_substitute(MAIL_FILE, buf, 256);
7345                 if (buf[0] != '\0') {
7346                         current_mail_spool = strndup(buf, text_buffer_size);
7347                 }
7348         }
7349
7350         no_buffers = 1;
7351         update_interval = 3.0;
7352         info.music_player_interval = 1.0;
7353         stuff_in_upper_case = 0;
7354         info.users.number = 1;
7355
7356 #ifdef TCP_PORT_MONITOR
7357         /* set default connection limit */
7358         tcp_portmon_set_max_connections(0);
7359 #endif
7360 }
7361
7362 /* returns 1 if you can overwrite or create the file at 'path' */
7363 static _Bool overwrite_works(const char *path)
7364 {
7365         FILE *filepointer;
7366
7367         if (!(filepointer = fopen(path, "w")))
7368                 return 0;
7369         fclose(filepointer);
7370         return 1;
7371 }
7372
7373 /* returns 1 if you can append or create the file at 'path' */
7374 static _Bool append_works(const char *path)
7375 {
7376         FILE *filepointer;
7377
7378         if (!(filepointer = fopen(path, "a")))
7379                 return 0;
7380         fclose(filepointer);
7381         return 1;
7382 }
7383
7384 #ifdef X11
7385 static void X11_initialisation(void)
7386 {
7387         if (x_initialised == YES) return;
7388         output_methods |= TO_X;
7389         init_X11(disp);
7390         set_default_configurations_for_x();
7391         x_initialised = YES;
7392 }
7393
7394 static void X11_destroy_window(void)
7395 {
7396         /* this function only exists for the sake of consistency */
7397         if (output_methods & TO_X) {
7398 #ifdef HAVE_XDAMAGE
7399                 XDamageDestroy(display, x11_stuff.damage);
7400                 XFixesDestroyRegion(display, x11_stuff.region2);
7401                 XFixesDestroyRegion(display, x11_stuff.part);
7402                 if (x11_stuff.region) {
7403                         XDestroyRegion(x11_stuff.region);
7404                 }
7405                 x11_stuff.region = NULL;
7406 #endif /* HAVE_XDAMAGE */
7407                 destroy_window();
7408         }
7409 }
7410
7411 static char **xargv = 0;
7412 static int xargc = 0;
7413
7414 static void X11_create_window(void)
7415 {
7416         if (output_methods & TO_X) {
7417 #ifdef OWN_WINDOW
7418                 init_window(own_window, text_width + border_margin * 2 + 1,
7419                                 text_height + border_margin * 2 + 1, set_transparent, background_colour,
7420                                 xargv, xargc);
7421 #else /* OWN_WINDOW */
7422                 init_window(0, text_width + border_margin * 2 + 1,
7423                                 text_height + border_margin * 2 + 1, set_transparent, 0,
7424                                 xargv, xargc);
7425 #endif /* OWN_WINDOW */
7426
7427                 selected_font = 0;
7428                 load_fonts();
7429                 update_text_area();     /* to position text/window on screen */
7430
7431 #ifdef OWN_WINDOW
7432                 if (own_window && !fixed_pos) {
7433                         XMoveWindow(display, window.window, window.x, window.y);
7434                 }
7435                 if (own_window) {
7436                         set_transparent_background(window.window);
7437                 }
7438 #endif
7439
7440                 create_gc();
7441
7442                 set_font();
7443                 draw_stuff();
7444
7445                 x11_stuff.region = XCreateRegion();
7446 #ifdef HAVE_XDAMAGE
7447                 if (!XDamageQueryExtension(display, &x11_stuff.event_base, &x11_stuff.error_base)) {
7448                         ERR("Xdamage extension unavailable");
7449                 }
7450                 x11_stuff.damage = XDamageCreate(display, window.window, XDamageReportNonEmpty);
7451                 x11_stuff.region2 = XFixesCreateRegionFromWindow(display, window.window, 0);
7452                 x11_stuff.part = XFixesCreateRegionFromWindow(display, window.window, 0);
7453 #endif /* HAVE_XDAMAGE */
7454
7455                 selected_font = 0;
7456                 update_text_area();     /* to get initial size of the window */
7457         }
7458 }
7459 #endif /* X11 */
7460
7461 #define CONF_ERR ERR("%s: %d: config file error", f, line)
7462 #define CONF_ERR2(a) ERR("%s: %d: config file error: %s", f, line, a)
7463 #define CONF2(a) if (strcasecmp(name, a) == 0)
7464 #define CONF(a) else CONF2(a)
7465 #define CONF3(a, b) else if (strcasecmp(name, a) == 0 \
7466                 || strcasecmp(name, b) == 0)
7467 #define CONF_CONTINUE 1
7468 #define CONF_BREAK 2
7469 #define CONF_BUFF_SIZE 512
7470
7471 static FILE *open_config_file(const char *f)
7472 {
7473 #ifdef CONFIG_OUTPUT
7474         if (!strcmp(f, "==builtin==")) {
7475 #ifdef HAVE_FOPENCOOKIE
7476                 return fopencookie(NULL, "r", conf_cookie);
7477 #endif /* HAVE_FOPENCOOKIE */
7478         } else
7479 #endif /* CONFIG_OUTPUT */
7480                 return fopen(f, "r");
7481 }
7482
7483 static int do_config_step(int *line, FILE *fp, char *buf, char **name, char **value)
7484 {
7485         char *p, *p2;
7486         (*line)++;
7487         if (fgets(buf, CONF_BUFF_SIZE, fp) == NULL) {
7488                 return CONF_BREAK;
7489         }
7490         remove_comments(buf);
7491
7492         p = buf;
7493
7494         /* skip spaces */
7495         while (*p && isspace((int) *p)) {
7496                 p++;
7497         }
7498         if (*p == '\0') {
7499                 return CONF_CONTINUE;   /* empty line */
7500         }
7501
7502         *name = p;
7503
7504         /* skip name */
7505         p2 = p;
7506         while (*p2 && !isspace((int) *p2)) {
7507                 p2++;
7508         }
7509         if (*p2 != '\0') {
7510                 *p2 = '\0';     /* break at name's end */
7511                 p2++;
7512         }
7513
7514         /* get value */
7515         if (*p2) {
7516                 p = p2;
7517                 while (*p && isspace((int) *p)) {
7518                         p++;
7519                 }
7520
7521                 *value = p;
7522
7523                 p2 = *value + strlen(*value);
7524                 while (isspace((int) *(p2 - 1))) {
7525                         *--p2 = '\0';
7526                 }
7527         } else {
7528                 *value = 0;
7529         }
7530         return 0;
7531 }
7532
7533 static void load_config_file(const char *f)
7534 {
7535         int line = 0;
7536         FILE *fp;
7537
7538         set_default_configurations();
7539         fp = open_config_file(f);
7540         if (!fp) {
7541                 return;
7542         }
7543         DBGP("reading contents from config file '%s'", f);
7544
7545         while (!feof(fp)) {
7546                 char buff[CONF_BUFF_SIZE], *name, *value;
7547                 int ret = do_config_step(&line, fp, buff, &name, &value);
7548                 if (ret == CONF_BREAK) {
7549                         break;
7550                 } else if (ret == CONF_CONTINUE) {
7551                         continue;
7552                 }
7553
7554 #ifdef X11
7555                 CONF2("out_to_x") {
7556                         /* don't listen if X is already initialised or
7557                          * if we already know we don't want it */
7558                         if(x_initialised == NO) {
7559                                 if (string_to_bool(value)) {
7560                                         output_methods &= TO_X;
7561                                 } else {
7562                                         output_methods &= ~TO_X;
7563                                         x_initialised = NEVER;
7564                                 }
7565                         }
7566                 }
7567                 CONF("display") {
7568                         if (!value || x_initialised == YES) {
7569                                 CONF_ERR;
7570                         } else {
7571                                 if (disp)
7572                                         free(disp);
7573                                 disp = strdup(value);
7574                         }
7575                 }
7576                 CONF("alignment") {
7577                         if (window.type == TYPE_DOCK)
7578                                 ;
7579                         else if (value) {
7580                                 int a = string_to_alignment(value);
7581
7582                                 if (a <= 0) {
7583                                         CONF_ERR;
7584                                 } else {
7585                                         text_alignment = a;
7586                                 }
7587                         } else {
7588                                 CONF_ERR;
7589                         }
7590                 }
7591                 CONF("background") {
7592                         fork_to_background = string_to_bool(value);
7593                 }
7594 #else
7595                 CONF2("background") {
7596                         fork_to_background = string_to_bool(value);
7597                 }
7598 #endif /* X11 */
7599 #ifdef X11
7600                 CONF("show_graph_scale") {
7601                         show_graph_scale = string_to_bool(value);
7602                 }
7603                 CONF("show_graph_range") {
7604                         show_graph_range = string_to_bool(value);
7605                 }
7606                 CONF("border_margin") {
7607                         if (value) {
7608                                 border_margin = strtol(value, 0, 0);
7609                         } else {
7610                                 CONF_ERR;
7611                         }
7612                 }
7613                 CONF("border_width") {
7614                         if (value) {
7615                                 border_width = strtol(value, 0, 0);
7616                         } else {
7617                                 CONF_ERR;
7618                         }
7619                 }
7620 #endif /* X11 */
7621 #define TEMPLATE_CONF(n) \
7622                 CONF("template"#n) { \
7623                         if (value) { \
7624                                 free(template[n]); \
7625                                 template[n] = strdup(value); \
7626                         } else { \
7627                                 CONF_ERR; \
7628                         } \
7629                 }
7630                 TEMPLATE_CONF(0)
7631                 TEMPLATE_CONF(1)
7632                 TEMPLATE_CONF(2)
7633                 TEMPLATE_CONF(3)
7634                 TEMPLATE_CONF(4)
7635                 TEMPLATE_CONF(5)
7636                 TEMPLATE_CONF(6)
7637                 TEMPLATE_CONF(7)
7638                 TEMPLATE_CONF(8)
7639                 TEMPLATE_CONF(9)
7640                 CONF("imap") {
7641                         if (value) {
7642                                 info.mail = parse_mail_args(IMAP_TYPE, value);
7643                         } else {
7644                                 CONF_ERR;
7645                         }
7646                 }
7647                 CONF("pop3") {
7648                         if (value) {
7649                                 info.mail = parse_mail_args(POP3_TYPE, value);
7650                         } else {
7651                                 CONF_ERR;
7652                         }
7653                 }
7654                 CONF("default_bar_size") {
7655                         char err = 0;
7656                         if (value) {
7657                                 if (sscanf(value, "%d %d", &default_bar_width, &default_bar_height) != 2) {
7658                                         err = 1;
7659                                 }
7660                         } else {
7661                                 err = 1;
7662                         }
7663                         if (err) {
7664                                 CONF_ERR2("default_bar_size takes 2 integer arguments (ie. 'default_bar_size 0 6')")
7665                         }
7666                 }
7667 #ifdef X11
7668                 CONF("default_graph_size") {
7669                         char err = 0;
7670                         if (value) {
7671                                 if (sscanf(value, "%d %d", &default_graph_width, &default_graph_height) != 2) {
7672                                         err = 1;
7673                                 }
7674                         } else {
7675                                 err = 1;
7676                         }
7677                         if (err) {
7678                                 CONF_ERR2("default_graph_size takes 2 integer arguments (ie. 'default_graph_size 0 6')")
7679                         }
7680                 }
7681                 CONF("default_gauge_size") {
7682                         char err = 0;
7683                         if (value) {
7684                                 if (sscanf(value, "%d %d", &default_gauge_width, &default_gauge_height) != 2) {
7685                                         err = 1;
7686                                 }
7687                         } else {
7688                                 err = 1;
7689                         }
7690                         if (err) {
7691                                 CONF_ERR2("default_gauge_size takes 2 integer arguments (ie. 'default_gauge_size 0 6')")
7692                         }
7693                 }
7694 #endif
7695 #ifdef MPD
7696                 CONF("mpd_host") {
7697                         if (value) {
7698                                 mpd_set_host(value);
7699                         } else {
7700                                 CONF_ERR;
7701                         }
7702                 }
7703                 CONF("mpd_port") {
7704                         if (value && mpd_set_port(value)) {
7705                                 CONF_ERR;
7706                         }
7707                 }
7708                 CONF("mpd_password") {
7709                         if (value) {
7710                                 mpd_set_password(value);
7711                         } else {
7712                                 CONF_ERR;
7713                         }
7714                 }
7715 #endif
7716                 CONF("music_player_interval") {
7717                         if (value) {
7718                                 info.music_player_interval = strtod(value, 0);
7719                         } else {
7720                                 CONF_ERR;
7721                         }
7722                 }
7723 #ifdef __OpenBSD__
7724                 CONF("sensor_device") {
7725                         if (value) {
7726                                 sensor_device = strtol(value, 0, 0);
7727                         } else {
7728                                 CONF_ERR;
7729                         }
7730                 }
7731 #endif
7732                 CONF("cpu_avg_samples") {
7733                         if (value) {
7734                                 cpu_avg_samples = strtol(value, 0, 0);
7735                                 if (cpu_avg_samples < 1 || cpu_avg_samples > 14) {
7736                                         CONF_ERR;
7737                                 } else {
7738                                         info.cpu_avg_samples = cpu_avg_samples;
7739                                 }
7740                         } else {
7741                                 CONF_ERR;
7742                         }
7743                 }
7744                 CONF("net_avg_samples") {
7745                         if (value) {
7746                                 net_avg_samples = strtol(value, 0, 0);
7747                                 if (net_avg_samples < 1 || net_avg_samples > 14) {
7748                                         CONF_ERR;
7749                                 } else {
7750                                         info.net_avg_samples = net_avg_samples;
7751                                 }
7752                         } else {
7753                                 CONF_ERR;
7754                         }
7755                 }
7756                 CONF("diskio_avg_samples") {
7757                         if (value) {
7758                                 diskio_avg_samples = strtol(value, 0, 0);
7759                                 if (diskio_avg_samples < 1 || diskio_avg_samples > 14) {
7760                                         CONF_ERR;
7761                                 } else {
7762                                         info.diskio_avg_samples = diskio_avg_samples;
7763                                 }
7764                         } else {
7765                                 CONF_ERR;
7766                         }
7767                 }
7768
7769 #ifdef HAVE_XDBE
7770                 CONF("double_buffer") {
7771                         use_xdbe = string_to_bool(value);
7772                 }
7773 #endif
7774 #ifdef X11
7775                 CONF("override_utf8_locale") {
7776                         utf8_mode = string_to_bool(value);
7777                 }
7778                 CONF("draw_borders") {
7779                         draw_borders = string_to_bool(value);
7780                 }
7781                 CONF("draw_graph_borders") {
7782                         draw_graph_borders = string_to_bool(value);
7783                 }
7784                 CONF("draw_shades") {
7785                         draw_shades = string_to_bool(value);
7786                 }
7787                 CONF("draw_outline") {
7788                         draw_outline = string_to_bool(value);
7789                 }
7790 #endif /* X11 */
7791                 CONF("out_to_console") {
7792                         if(string_to_bool(value))
7793                                 output_methods |= TO_STDOUT;
7794                 }
7795                 CONF("out_to_stderr") {
7796                         if(string_to_bool(value))
7797                                 output_methods |= TO_STDERR;
7798                 }
7799                 CONF("overwrite_file") {
7800                         if(overwrite_file) {
7801                                 free(overwrite_file);
7802                                 overwrite_file = 0;
7803                         }
7804                         if(overwrite_works(value)) {
7805                                 overwrite_file = strdup(value);
7806                                 output_methods |= OVERWRITE_FILE;
7807                         } else
7808                                 ERR("overwrite_file won't be able to create/overwrite '%s'", value);
7809                 }
7810                 CONF("append_file") {
7811                         if(append_file) {
7812                                 free(append_file);
7813                                 append_file = 0;
7814                         }
7815                         if(append_works(value)) {
7816                                 append_file = strdup(value);
7817                                 output_methods |= APPEND_FILE;
7818                         } else
7819                                 ERR("append_file won't be able to create/append '%s'", value);
7820                 }
7821                 CONF("use_spacer") {
7822                         if (value) {
7823                                 if (strcasecmp(value, "left") == EQUAL) {
7824                                         use_spacer = LEFT_SPACER;
7825                                 } else if (strcasecmp(value, "right") == EQUAL) {
7826                                         use_spacer = RIGHT_SPACER;
7827                                 } else if (strcasecmp(value, "none") == EQUAL) {
7828                                         use_spacer = NO_SPACER;
7829                                 } else {
7830                                         use_spacer = string_to_bool(value);
7831                                         ERR("use_spacer should have an argument of left, right, or"
7832                                                 " none.  '%s' seems to be some form of '%s', so"
7833                                                 " defaulting to %s.", value,
7834                                                 use_spacer ? "true" : "false",
7835                                                 use_spacer ? "right" : "none");
7836                                         if (use_spacer) {
7837                                                 use_spacer = RIGHT_SPACER;
7838                                         } else {
7839                                                 use_spacer = NO_SPACER;
7840                                         }
7841                                 }
7842                         } else {
7843                                 ERR("use_spacer should have an argument. Defaulting to right.");
7844                                 use_spacer = RIGHT_SPACER;
7845                         }
7846                 }
7847 #ifdef X11
7848 #ifdef XFT
7849                 CONF("use_xft") {
7850                         use_xft = string_to_bool(value);
7851                 }
7852                 CONF("font") {
7853                         if (value) {
7854                                 set_first_font(value);
7855                         }
7856                 }
7857                 CONF("xftalpha") {
7858                         if (value && font_count >= 0) {
7859                                 fonts[0].font_alpha = atof(value) * 65535.0;
7860                         }
7861                 }
7862                 CONF("xftfont") {
7863                         if (use_xft) {
7864 #else
7865                 CONF("use_xft") {
7866                         if (string_to_bool(value)) {
7867                                 ERR("Xft not enabled");
7868                         }
7869                 }
7870                 CONF("xftfont") {
7871                         /* xftfont silently ignored when no Xft */
7872                 }
7873                 CONF("xftalpha") {
7874                         /* xftalpha is silently ignored when no Xft */
7875                 }
7876                 CONF("font") {
7877 #endif
7878                         if (value) {
7879                                 set_first_font(value);
7880                         }
7881 #ifdef XFT
7882                         }
7883 #endif
7884                 }
7885                 CONF("gap_x") {
7886                         if (value) {
7887                                 gap_x = atoi(value);
7888                         } else {
7889                                 CONF_ERR;
7890                         }
7891                 }
7892                 CONF("gap_y") {
7893                         if (value) {
7894                                 gap_y = atoi(value);
7895                         } else {
7896                                 CONF_ERR;
7897                         }
7898                 }
7899 #endif /* X11 */
7900                 CONF("mail_spool") {
7901                         if (value) {
7902                                 char buffer[256];
7903
7904                                 variable_substitute(value, buffer, 256);
7905
7906                                 if (buffer[0] != '\0') {
7907                                         if (current_mail_spool) {
7908                                                 free(current_mail_spool);
7909                                         }
7910                                         current_mail_spool = strndup(buffer, text_buffer_size);
7911                                 }
7912                         } else {
7913                                 CONF_ERR;
7914                         }
7915                 }
7916 #ifdef X11
7917                 CONF("minimum_size") {
7918                         if (value) {
7919                                 if (sscanf(value, "%d %d", &minimum_width, &minimum_height)
7920                                                 != 2) {
7921                                         if (sscanf(value, "%d", &minimum_width) != 1) {
7922                                                 CONF_ERR;
7923                                         }
7924                                 }
7925                         } else {
7926                                 CONF_ERR;
7927                         }
7928                 }
7929                 CONF("maximum_width") {
7930                         if (value) {
7931                                 if (sscanf(value, "%d", &maximum_width) != 1) {
7932                                         CONF_ERR;
7933                                 }
7934                         } else {
7935                                 CONF_ERR;
7936                         }
7937                 }
7938 #endif /* X11 */
7939                 CONF("no_buffers") {
7940                         no_buffers = string_to_bool(value);
7941                 }
7942                 CONF("top_name_width") {
7943                         if (value) {
7944                                 if (sscanf(value, "%u", &top_name_width) != 1) {
7945                                         CONF_ERR;
7946                                 }
7947                         } else {
7948                                 CONF_ERR;
7949                         }
7950                         if (top_name_width >= max_user_text) {
7951                                 top_name_width = max_user_text - 1;
7952                         }
7953                 }
7954                 CONF("top_cpu_separate") {
7955                         cpu_separate = string_to_bool(value);
7956                 }
7957                 CONF("short_units") {
7958                         short_units = string_to_bool(value);
7959                 }
7960                 CONF("pad_percents") {
7961                         pad_percents = atoi(value);
7962                 }
7963 #ifdef X11
7964 #ifdef OWN_WINDOW
7965                 CONF("own_window") {
7966                         if (value) {
7967                                 own_window = string_to_bool(value);
7968                         }
7969                 }
7970                 CONF("own_window_class") {
7971                         if (value) {
7972                                 memset(window.class_name, 0, sizeof(window.class_name));
7973                                 strncpy(window.class_name, value,
7974                                                 sizeof(window.class_name) - 1);
7975                         }
7976                 }
7977                 CONF("own_window_title") {
7978                         if (value) {
7979                                 memset(window.title, 0, sizeof(window.title));
7980                                 strncpy(window.title, value, sizeof(window.title) - 1);
7981                         }
7982                 }
7983                 CONF("own_window_transparent") {
7984                         if (value) {
7985                                 set_transparent = string_to_bool(value);
7986                         }
7987                 }
7988                 CONF("own_window_hints") {
7989                         if (value) {
7990                                 char *p_hint, *p_save;
7991                                 char delim[] = ", ";
7992
7993                                 /* tokenize the value into individual hints */
7994                                 if ((p_hint = strtok_r(value, delim, &p_save)) != NULL) {
7995                                         do {
7996                                                 /* fprintf(stderr, "hint [%s] parsed\n", p_hint); */
7997                                                 if (strncmp(p_hint, "undecorate", 10) == EQUAL) {
7998                                                         SET_HINT(window.hints, HINT_UNDECORATED);
7999                                                 } else if (strncmp(p_hint, "below", 5) == EQUAL) {
8000                                                         SET_HINT(window.hints, HINT_BELOW);
8001                                                 } else if (strncmp(p_hint, "above", 5) == EQUAL) {
8002                                                         SET_HINT(window.hints, HINT_ABOVE);
8003                                                 } else if (strncmp(p_hint, "sticky", 6) == EQUAL) {
8004                                                         SET_HINT(window.hints, HINT_STICKY);
8005                                                 } else if (strncmp(p_hint, "skip_taskbar", 12) == EQUAL) {
8006                                                         SET_HINT(window.hints, HINT_SKIP_TASKBAR);
8007                                                 } else if (strncmp(p_hint, "skip_pager", 10) == EQUAL) {
8008                                                         SET_HINT(window.hints, HINT_SKIP_PAGER);
8009                                                 } else {
8010                                                         CONF_ERR;
8011                                                 }
8012
8013                                                 p_hint = strtok_r(NULL, delim, &p_save);
8014                                         } while (p_hint != NULL);
8015                                 }
8016                         } else {
8017                                 CONF_ERR;
8018                         }
8019                 }
8020                 CONF("own_window_type") {
8021                         if (value) {
8022                                 if (strncmp(value, "normal", 6) == EQUAL) {
8023                                         window.type = TYPE_NORMAL;
8024                                 } else if (strncmp(value, "desktop", 7) == EQUAL) {
8025                                         window.type = TYPE_DESKTOP;
8026                                 } else if (strncmp(value, "dock", 7) == EQUAL) {
8027                                         window.type = TYPE_DOCK;
8028                                         text_alignment = TOP_LEFT;
8029                                 } else if (strncmp(value, "override", 8) == EQUAL) {
8030                                         window.type = TYPE_OVERRIDE;
8031                                 } else {
8032                                         CONF_ERR;
8033                                 }
8034                         } else {
8035                                 CONF_ERR;
8036                         }
8037                 }
8038 #endif
8039                 CONF("stippled_borders") {
8040                         if (value) {
8041                                 stippled_borders = strtol(value, 0, 0);
8042                         } else {
8043                                 stippled_borders = 4;
8044                         }
8045                 }
8046 #ifdef IMLIB2
8047                 CONF("imlib_cache_size") {
8048                         if (value) {
8049                                 cimlib_set_cache_size(atoi(value));
8050                         }
8051                 }
8052                 CONF("imlib_cache_flush_interval") {
8053                         if (value) {
8054                                 cimlib_set_cache_flush_interval(atoi(value));
8055                         }
8056                 }
8057 #endif /* IMLIB2 */
8058 #endif /* X11 */
8059                 CONF("update_interval") {
8060                         if (value) {
8061                                 update_interval = strtod(value, 0);
8062                         } else {
8063                                 CONF_ERR;
8064                         }
8065                         if (info.music_player_interval == 0) {
8066                                 // default to update_interval
8067                                 info.music_player_interval = update_interval;
8068                         }
8069                 }
8070                 CONF("total_run_times") {
8071                         if (value) {
8072                                 total_run_times = strtod(value, 0);
8073                         } else {
8074                                 CONF_ERR;
8075                         }
8076                 }
8077                 CONF("uppercase") {
8078                         stuff_in_upper_case = string_to_bool(value);
8079                 }
8080                 CONF("max_specials") {
8081                         if (value) {
8082                                 max_specials = atoi(value);
8083                         } else {
8084                                 CONF_ERR;
8085                         }
8086                 }
8087                 CONF("max_user_text") {
8088                         if (value) {
8089                                 max_user_text = atoi(value);
8090                         } else {
8091                                 CONF_ERR;
8092                         }
8093                 }
8094                 CONF("text_buffer_size") {
8095                         if (value) {
8096                                 text_buffer_size = atoi(value);
8097                                 if (text_buffer_size < DEFAULT_TEXT_BUFFER_SIZE) {
8098                                         ERR("text_buffer_size must be >=%i bytes", DEFAULT_TEXT_BUFFER_SIZE);
8099                                         text_buffer_size = DEFAULT_TEXT_BUFFER_SIZE;
8100                                 }
8101                         } else {
8102                                 CONF_ERR;
8103                         }
8104                 }
8105                 CONF("text") {
8106 #ifdef X11
8107                         if (output_methods & TO_X) {
8108                                 X11_initialisation();
8109                         }
8110 #endif
8111
8112                         if (global_text) {
8113                                 free(global_text);
8114                                 global_text = 0;
8115                         }
8116
8117                         global_text = (char *) malloc(1);
8118                         global_text[0] = '\0';
8119
8120                         while (!feof(fp)) {
8121                                 unsigned int l = strlen(global_text);
8122                                 unsigned int bl;
8123                                 char buf[CONF_BUFF_SIZE];
8124
8125                                 if (fgets(buf, CONF_BUFF_SIZE, fp) == NULL) {
8126                                         break;
8127                                 }
8128
8129                                 /* Remove \\-\n. */
8130                                 bl = strlen(buf);
8131                                 if (bl >= 2 && buf[bl-2] == '\\' && buf[bl-1] == '\n') {
8132                                         buf[bl-2] = '\0';
8133                                         bl -= 2;
8134                                         if (bl == 0) {
8135                                                 continue;
8136                                         }
8137                                 }
8138
8139                                 /* Check for continuation of \\-\n. */
8140                                 if (l > 0 && buf[0] == '\n' && global_text[l-1] == '\\') {
8141                                         global_text[l-1] = '\0';
8142                                         continue;
8143                                 }
8144
8145                                 global_text = (char *) realloc(global_text, l + bl + 1);
8146                                 strcat(global_text, buf);
8147
8148                                 if (strlen(global_text) > max_user_text) {
8149                                         break;
8150                                 }
8151                         }
8152                         fclose(fp);
8153                         if (strlen(global_text) < 1) {
8154                                 CRIT_ERR("no text supplied in configuration; exiting");
8155                         }
8156                         global_text_lines = line + 1;
8157                         return;
8158                 }
8159 #ifdef TCP_PORT_MONITOR
8160                 CONF("max_port_monitor_connections") {
8161                         int max;
8162                         if (!value || (sscanf(value, "%d", &max) != 1)) {
8163                                 /* an error. use default, warn and continue. */
8164                                 tcp_portmon_set_max_connections(0);
8165                                 CONF_ERR;
8166                         } else if (tcp_portmon_set_max_connections(max)) {
8167                                 /* max is < 0, default has been set*/
8168                                 CONF_ERR;
8169                         }
8170                 }
8171 #endif
8172                 CONF("if_up_strictness") {
8173                         if (!value) {
8174                                 ERR("incorrect if_up_strictness value, defaulting to 'up'");
8175                                 ifup_strictness = IFUP_UP;
8176                         } else if (strcasecmp(value, "up") == EQUAL) {
8177                                 ifup_strictness = IFUP_UP;
8178                         } else if (strcasecmp(value, "link") == EQUAL) {
8179                                 ifup_strictness = IFUP_LINK;
8180                         } else if (strcasecmp(value, "address") == EQUAL) {
8181                                 ifup_strictness = IFUP_ADDR;
8182                         } else {
8183                                 ERR("incorrect if_up_strictness value, defaulting to 'up'");
8184                                 ifup_strictness = IFUP_UP;
8185                         }
8186                 }
8187
8188                 CONF("temperature_unit") {
8189                         if (!value) {
8190                                 ERR("config option 'temperature_unit' needs an argument, either 'celsius' or 'fahrenheit'");
8191                         } else if (set_temp_output_unit(value)) {
8192                                 ERR("temperature_unit: incorrect argument");
8193                         }
8194                 }
8195
8196                 CONF("alias") {
8197                         if (value) {
8198                                 size_t maxlength = strlen(value);       //+1 for terminating 0 not needed, 'cause of the space in the middle of value
8199                                 char *skey = malloc(maxlength);
8200                                 char *svalue = malloc(maxlength);
8201                                 char *oldvalue;
8202                                 if (sscanf(value, "%[0-9a-zA-Z_] %[^\n]", skey, svalue) == 2) {
8203                                         oldvalue = getenv(skey);
8204                                         if (oldvalue == NULL) {
8205                                                 setenv(skey, svalue, 0);
8206                                         }
8207                                         //PS: Don't free oldvalue, it's the real envvar, not a copy
8208                                 } else {
8209                                         CONF_ERR;
8210                                 }
8211                                 free(skey);
8212                                 free(svalue);
8213                         } else {
8214                                 CONF_ERR;
8215                         }
8216                 }
8217 #ifdef HAVE_LUA
8218                 CONF("lua_load") {
8219                         llua_init();
8220                         if(value) {
8221                                 char *ptr = strtok(value, " ");
8222                                 while(ptr) {
8223                                         llua_load(ptr);
8224                                         ptr = strtok(NULL, " ");
8225                                 }
8226                         } else {
8227                                 CONF_ERR;
8228                         }
8229                 }
8230 #endif /* HAVE_LUA */
8231
8232                 CONF("color0"){}
8233                 CONF("color1"){}
8234                 CONF("color2"){}
8235                 CONF("color3"){}
8236                 CONF("color4"){}
8237                 CONF("color5"){}
8238                 CONF("color6"){}
8239                 CONF("color7"){}
8240                 CONF("color8"){}
8241                 CONF("color9"){}
8242                 CONF("default_color"){}
8243                 CONF3("default_shade_color", "default_shadecolor"){}
8244                 CONF3("default_outline_color", "default_outlinecolor") {}
8245                 CONF("own_window_colour") {}
8246
8247                 else {
8248                         ERR("%s: %d: no such configuration: '%s'", f, line, name);
8249                 }
8250         }
8251
8252         fclose(fp);
8253
8254         if (info.music_player_interval == 0) {
8255                 // default to update_interval
8256                 info.music_player_interval = update_interval;
8257         }
8258         if (!global_text) { // didn't supply any text
8259                 CRIT_ERR("missing text block in configuration; exiting");
8260         }
8261 }
8262
8263 static void load_config_file_x11(const char *f)
8264 {
8265         int line = 0;
8266         FILE *fp;
8267
8268         fp = open_config_file(f);
8269         if (!fp) {
8270                 return;
8271         }
8272         DBGP("reading contents from config file '%s'", f);
8273
8274         while (!feof(fp)) {
8275                 char buff[CONF_BUFF_SIZE], *name, *value;
8276                 int ret = do_config_step(&line, fp, buff, &name, &value);
8277                 if (ret == CONF_BREAK) {
8278                         break;
8279                 } else if (ret == CONF_CONTINUE) {
8280                         continue;
8281                 }
8282
8283 #ifdef X11
8284                 CONF2("color0") {
8285                         X11_initialisation();
8286                         if (x_initialised == YES) {
8287                                 if (value) {
8288                                         color0 = get_x11_color(value);
8289                                 } else {
8290                                         CONF_ERR;
8291                                 }
8292                         }
8293                 }
8294                 CONF("color1") {
8295                         X11_initialisation();
8296                         if (x_initialised == YES) {
8297                                 if (value) {
8298                                         color1 = get_x11_color(value);
8299                                 } else {
8300                                         CONF_ERR;
8301                                 }
8302                         }
8303                 }
8304                 CONF("color2") {
8305                         X11_initialisation();
8306                         if (x_initialised == YES) {
8307                                 if (value) {
8308                                         color2 = get_x11_color(value);
8309                                 } else {
8310                                         CONF_ERR;
8311                                 }
8312                         }
8313                 }
8314                 CONF("color3") {
8315                         X11_initialisation();
8316                         if (x_initialised == YES) {
8317                                 if (value) {
8318                                         color3 = get_x11_color(value);
8319                                 } else {
8320                                         CONF_ERR;
8321                                 }
8322                         }
8323                 }
8324                 CONF("color4") {
8325                         X11_initialisation();
8326                         if (x_initialised == YES) {
8327                                 if (value) {
8328                                         color4 = get_x11_color(value);
8329                                 } else {
8330                                         CONF_ERR;
8331                                 }
8332                         }
8333                 }
8334                 CONF("color5") {
8335                         X11_initialisation();
8336                         if (x_initialised == YES) {
8337                                 if (value) {
8338                                         color5 = get_x11_color(value);
8339                                 } else {
8340                                         CONF_ERR;
8341                                 }
8342                         }
8343                 }
8344                 CONF("color6") {
8345                         X11_initialisation();
8346                         if (x_initialised == YES) {
8347                                 if (value) {
8348                                         color6 = get_x11_color(value);
8349                                 } else {
8350                                         CONF_ERR;
8351                                 }
8352                         }
8353                 }
8354                 CONF("color7") {
8355                         X11_initialisation();
8356                         if (x_initialised == YES) {
8357                                 if (value) {
8358                                         color7 = get_x11_color(value);
8359                                 } else {
8360                                         CONF_ERR;
8361                                 }
8362                         }
8363                 }
8364                 CONF("color8") {
8365                         X11_initialisation();
8366                         if (x_initialised == YES) {
8367                                 if (value) {
8368                                         color8 = get_x11_color(value);
8369                                 } else {
8370                                         CONF_ERR;
8371                                 }
8372                         }
8373                 }
8374                 CONF("color9") {
8375                         X11_initialisation();
8376                         if (x_initialised == YES) {
8377                                 if (value) {
8378                                         color9 = get_x11_color(value);
8379                                 } else {
8380                                         CONF_ERR;
8381                                 }
8382                         }
8383                 }
8384                 CONF("default_color") {
8385                         X11_initialisation();
8386                         if (x_initialised == YES) {
8387                                 if (value) {
8388                                         default_fg_color = get_x11_color(value);
8389                                 } else {
8390                                         CONF_ERR;
8391                                 }
8392                         }
8393                 }
8394                 CONF3("default_shade_color", "default_shadecolor") {
8395                         X11_initialisation();
8396                         if (x_initialised == YES) {
8397                                 if (value) {
8398                                         default_bg_color = get_x11_color(value);
8399                                 } else {
8400                                         CONF_ERR;
8401                                 }
8402                         }
8403                 }
8404                 CONF3("default_outline_color", "default_outlinecolor") {
8405                         X11_initialisation();
8406                         if (x_initialised == YES) {
8407                                 if (value) {
8408                                         default_out_color = get_x11_color(value);
8409                                 } else {
8410                                         CONF_ERR;
8411                                 }
8412                         }
8413                 }
8414 #ifdef OWN_WINDOW
8415                 CONF("own_window_colour") {
8416                         X11_initialisation();
8417                         if (x_initialised == YES) {
8418                                 if (value) {
8419                                         background_colour = get_x11_color(value);
8420                                 } else {
8421                                         ERR("Invalid colour for own_window_colour (try omitting the "
8422                                                 "'#' for hex colours");
8423                                 }
8424                         }
8425                 }
8426 #endif
8427                 CONF("text") {
8428                         //initialize X11 if nothing X11-related is mentioned before TEXT (and if X11 is the default outputmethod)
8429                         if(output_methods & TO_X) {
8430                                 X11_initialisation();
8431                         }
8432                 }
8433 #endif /* X11 */
8434 #undef CONF
8435 #undef CONF2
8436 #undef CONF3
8437 #undef CONF_ERR
8438 #undef CONF_ERR2
8439 #undef CONF_BREAK
8440 #undef CONF_CONTINUE
8441 #undef CONF_BUFF_SIZE
8442         }
8443
8444         fclose(fp);
8445
8446 }
8447
8448 static void print_help(const char *prog_name) {
8449         printf("Usage: %s [OPTION]...\n"
8450                         PACKAGE_NAME" is a system monitor that renders text on desktop or to own transparent\n"
8451                         "window. Command line options will override configurations defined in config\n"
8452                         "file.\n"
8453                         "   -v, --version             version\n"
8454                         "   -q, --quiet               quiet mode\n"
8455                         "   -D, --debug               increase debugging output\n"
8456                         "   -c, --config=FILE         config file to load\n"
8457 #ifdef CONFIG_OUTPUT
8458                         "   -C, --print-config        print the builtin default config to stdout\n"
8459                         "                             e.g. 'conky -C > ~/.conkyrc' will create a new default config\n"
8460 #endif
8461                         "   -d, --daemonize           daemonize, fork to background\n"
8462                         "   -h, --help                help\n"
8463 #ifdef X11
8464                         "   -a, --alignment=ALIGNMENT text alignment on screen, {top,bottom,middle}_{left,right,middle}\n"
8465                         "   -f, --font=FONT           font to use\n"
8466                         "   -X, --display=DISPLAY     X11 display to use\n"
8467 #ifdef OWN_WINDOW
8468                         "   -o, --own-window          create own window to draw\n"
8469 #endif
8470 #ifdef HAVE_XDBE
8471                         "   -b, --double-buffer       double buffer (prevents flickering)\n"
8472 #endif
8473                         "   -w, --window-id=WIN_ID    window id to draw\n"
8474                         "   -x X                      x position\n"
8475                         "   -y Y                      y position\n"
8476 #endif /* X11 */
8477                         "   -t, --text=TEXT           text to render, remember single quotes, like -t '$uptime'\n"
8478                         "   -u, --interval=SECS       update interval\n"
8479                         "   -i COUNT                  number of times to update "PACKAGE_NAME" (and quit)\n",
8480                         prog_name
8481         );
8482 }
8483
8484 /* : means that character before that takes an argument */
8485 static const char *getopt_string = "vVqdDt:u:i:hc:"
8486 #ifdef X11
8487         "x:y:w:a:f:X:"
8488 #ifdef OWN_WINDOW
8489         "o"
8490 #endif
8491 #ifdef HAVE_XDBE
8492         "b"
8493 #endif
8494 #endif /* X11 */
8495 #ifdef CONFIG_OUTPUT
8496         "C"
8497 #endif
8498         ;
8499
8500 static const struct option longopts[] = {
8501         { "help", 0, NULL, 'h' },
8502         { "version", 0, NULL, 'V' },
8503         { "debug", 0, NULL, 'D' },
8504         { "config", 1, NULL, 'c' },
8505 #ifdef CONFIG_OUTPUT
8506         { "print-config", 0, NULL, 'C' },
8507 #endif
8508         { "daemonize", 0, NULL, 'd' },
8509 #ifdef X11
8510         { "alignment", 1, NULL, 'a' },
8511         { "font", 1, NULL, 'f' },
8512         { "display", 1, NULL, 'X' },
8513 #ifdef OWN_WINDOW
8514         { "own-window", 0, NULL, 'o' },
8515 #endif
8516 #ifdef HAVE_XDBE
8517         { "double-buffer", 0, NULL, 'b' },
8518 #endif
8519         { "window-id", 1, NULL, 'w' },
8520 #endif /* X11 */
8521         { "text", 1, NULL, 't' },
8522         { "interval", 0, NULL, 'u' },
8523         { 0, 0, 0, 0 }
8524 };
8525
8526 int main(int argc, char **argv)
8527 {
8528 #ifdef X11
8529         char *s, *temp;
8530         unsigned int x;
8531 #endif
8532         struct sigaction act, oact;
8533
8534         g_signal_pending = 0;
8535         memset(&info, 0, sizeof(info));
8536         clear_net_stats();
8537
8538 #ifdef TCP_PORT_MONITOR
8539         /* set default connection limit */
8540         tcp_portmon_set_max_connections(0);
8541 #endif
8542
8543         /* handle command line parameters that don't change configs */
8544 #ifdef X11
8545         if (((s = getenv("LC_ALL")) && *s) || ((s = getenv("LC_CTYPE")) && *s)
8546                         || ((s = getenv("LANG")) && *s)) {
8547                 temp = (char *) malloc((strlen(s) + 1) * sizeof(char));
8548                 if (temp == NULL) {
8549                         ERR("malloc failed");
8550                 }
8551                 for (x = 0; x < strlen(s); x++) {
8552                         temp[x] = tolower(s[x]);
8553                 }
8554                 temp[x] = 0;
8555                 if (strstr(temp, "utf-8") || strstr(temp, "utf8")) {
8556                         utf8_mode = 1;
8557                 }
8558
8559                 free(temp);
8560         }
8561         if (!setlocale(LC_CTYPE, "")) {
8562                 ERR("Can't set the specified locale!\nCheck LANG, LC_CTYPE, LC_ALL.");
8563         }
8564 #endif /* X11 */
8565         while (1) {
8566                 int c = getopt_long(argc, argv, getopt_string, longopts, NULL);
8567
8568                 if (c == -1) {
8569                         break;
8570                 }
8571
8572                 switch (c) {
8573                         case 'v':
8574                         case 'V':
8575                                 print_version();
8576                         case 'c':
8577                                 if (current_config) {
8578                                         free(current_config);
8579                                 }
8580                                 current_config = strndup(optarg, max_user_text);
8581                                 break;
8582                         case 'q':
8583                                 freopen("/dev/null", "w", stderr);
8584                                 break;
8585                         case 'h':
8586                                 print_help(argv[0]);
8587                                 return 0;
8588 #ifdef CONFIG_OUTPUT
8589                         case 'C':
8590                                 print_defconfig();
8591                                 return 0;
8592 #endif
8593 #ifdef X11
8594                         case 'w':
8595                                 window.window = strtol(optarg, 0, 0);
8596                                 break;
8597 #endif /* X11 */
8598
8599                         case '?':
8600                                 exit(EXIT_FAILURE);
8601                 }
8602         }
8603
8604         /* check if specified config file is valid */
8605         if (current_config) {
8606                 struct stat sb;
8607                 if (stat(current_config, &sb) ||
8608                                 (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))) {
8609                         ERR("invalid configuration file '%s'\n", current_config);
8610                         free(current_config);
8611                         current_config = 0;
8612                 }
8613         }
8614
8615         /* load current_config, CONFIG_FILE or SYSTEM_CONFIG_FILE */
8616
8617         if (!current_config) {
8618                 /* load default config file */
8619                 char buf[DEFAULT_TEXT_BUFFER_SIZE];
8620                 FILE *fp;
8621
8622                 /* Try to use personal config file first */
8623                 to_real_path(buf, CONFIG_FILE);
8624                 if (buf[0] && (fp = fopen(buf, "r"))) {
8625                         current_config = strndup(buf, max_user_text);
8626                         fclose(fp);
8627                 }
8628
8629                 /* Try to use system config file if personal config not readable */
8630                 if (!current_config && (fp = fopen(SYSTEM_CONFIG_FILE, "r"))) {
8631                         current_config = strndup(SYSTEM_CONFIG_FILE, max_user_text);
8632                         fclose(fp);
8633                 }
8634
8635                 /* No readable config found */
8636                 if (!current_config) {
8637 #ifdef CONFIG_OUTPUT
8638                         current_config = strdup("==builtin==");
8639                         ERR("no readable personal or system-wide config file found,"
8640                                         " using builtin default");
8641 #else
8642                         CRIT_ERR("no readable personal or system-wide config file found");
8643 #endif /* ! CONF_OUTPUT */
8644                 }
8645         }
8646 #ifdef HAVE_SYS_INOTIFY_H
8647         inotify_fd = inotify_init();
8648 #endif /* HAVE_SYS_INOTIFY_H */
8649
8650         load_config_file(current_config);
8651
8652         /* init specials array */
8653         if ((specials = calloc(sizeof(struct special_t), max_specials)) == 0) {
8654                 ERR("failed to create specials array");
8655         }
8656
8657 #ifdef MAIL_FILE
8658         if (current_mail_spool == NULL) {
8659                 char buf[256];
8660
8661                 variable_substitute(MAIL_FILE, buf, 256);
8662
8663                 if (buf[0] != '\0') {
8664                         current_mail_spool = strndup(buf, text_buffer_size);
8665                 }
8666         }
8667 #endif
8668
8669         /* handle other command line arguments */
8670
8671 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) \
8672                 || defined(__NetBSD__)
8673         optind = optreset = 1;
8674 #else
8675         optind = 0;
8676 #endif
8677
8678 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
8679         if ((kd = kvm_open("/dev/null", "/dev/null", "/dev/null", O_RDONLY,
8680                         "kvm_open")) == NULL) {
8681                 CRIT_ERR("cannot read kvm");
8682         }
8683 #endif
8684
8685         while (1) {
8686                 int c = getopt_long(argc, argv, getopt_string, longopts, NULL);
8687
8688                 if (c == -1) {
8689                         break;
8690                 }
8691
8692                 switch (c) {
8693                         case 'd':
8694                                 fork_to_background = 1;
8695                                 break;
8696                         case 'D':
8697                                 global_debug_level++;
8698                                 break;
8699 #ifdef X11
8700                         case 'f':
8701                                 set_first_font(optarg);
8702                                 break;
8703                         case 'a':
8704                                 text_alignment = string_to_alignment(optarg);
8705                                 break;
8706                         case 'X':
8707                                 if (disp)
8708                                         free(disp);
8709                                 disp = strdup(optarg);
8710                                 break;
8711
8712 #ifdef OWN_WINDOW
8713                         case 'o':
8714                                 own_window = 1;
8715                                 break;
8716 #endif
8717 #ifdef HAVE_XDBE
8718                         case 'b':
8719                                 use_xdbe = 1;
8720                                 break;
8721 #endif
8722 #endif /* X11 */
8723                         case 't':
8724                                 if (global_text) {
8725                                         free(global_text);
8726                                         global_text = 0;
8727                                 }
8728                                 global_text = strndup(optarg, max_user_text);
8729                                 convert_escapes(global_text);
8730                                 break;
8731
8732                         case 'u':
8733                                 update_interval = strtod(optarg, 0);
8734                                 if (info.music_player_interval == 0) {
8735                                         // default to update_interval
8736                                         info.music_player_interval = update_interval;
8737                                 }
8738                                 break;
8739
8740                         case 'i':
8741                                 total_run_times = strtod(optarg, 0);
8742                                 break;
8743 #ifdef X11
8744                         case 'x':
8745                                 gap_x = atoi(optarg);
8746                                 break;
8747
8748                         case 'y':
8749                                 gap_y = atoi(optarg);
8750                                 break;
8751 #endif /* X11 */
8752
8753                         case '?':
8754                                 exit(EXIT_FAILURE);
8755                 }
8756         }
8757
8758 #ifdef X11
8759         /* load font */
8760         if (output_methods & TO_X) {
8761                 load_config_file_x11(current_config);
8762         }
8763 #endif /* X11 */
8764
8765         /* generate text and get initial size */
8766         extract_variable_text(global_text);
8767         if (global_text) {
8768                 free(global_text);
8769                 global_text = 0;
8770         }
8771         global_text = NULL;
8772         /* fork */
8773         if (fork_to_background) {
8774                 int pid = fork();
8775
8776                 switch (pid) {
8777                         case -1:
8778                                 ERR(PACKAGE_NAME": couldn't fork() to background: %s",
8779                                         strerror(errno));
8780                                 break;
8781
8782                         case 0:
8783                                 /* child process */
8784                                 usleep(25000);
8785                                 fprintf(stderr, "\n");
8786                                 fflush(stderr);
8787                                 break;
8788
8789                         default:
8790                                 /* parent process */
8791                                 fprintf(stderr, PACKAGE_NAME": forked to background, pid is %d\n",
8792                                         pid);
8793                                 fflush(stderr);
8794                                 return 0;
8795                 }
8796         }
8797
8798         text_buffer = malloc(max_user_text);
8799         memset(text_buffer, 0, max_user_text);
8800         tmpstring1 = malloc(text_buffer_size);
8801         memset(tmpstring1, 0, text_buffer_size);
8802         tmpstring2 = malloc(text_buffer_size);
8803         memset(tmpstring2, 0, text_buffer_size);
8804
8805 #ifdef X11
8806         xargc = argc;
8807         xargv = argv;
8808         X11_create_window();
8809 #endif /* X11 */
8810
8811         /* Set signal handlers */
8812         act.sa_handler = signal_handler;
8813         sigemptyset(&act.sa_mask);
8814         act.sa_flags = 0;
8815 #ifdef SA_RESTART
8816         act.sa_flags |= SA_RESTART;
8817 #endif
8818
8819         if (            sigaction(SIGINT,  &act, &oact) < 0
8820                         ||      sigaction(SIGUSR1, &act, &oact) < 0
8821                         ||      sigaction(SIGHUP,  &act, &oact) < 0
8822                         ||      sigaction(SIGTERM, &act, &oact) < 0) {
8823                 ERR("error setting signal handler: %s", strerror(errno));
8824         }
8825
8826         main_loop();
8827
8828 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
8829         kvm_close(kd);
8830 #endif
8831
8832         return 0;
8833 }
8834
8835 static void signal_handler(int sig)
8836 {
8837         /* signal handler is light as a feather, as it should be.
8838          * we will poll g_signal_pending with each loop of conky
8839          * and do any signal processing there, NOT here. */
8840         g_signal_pending = sig;
8841 }