934c8d5631c487b68f96585d18deded28a870fa1
[monky] / conky.c
1 /*
2  * Conky, a system monitor, based on torsmo
3  *
4  * This program is licensed under BSD license, read COPYING
5  *
6  *  $Id$
7  */
8
9 #include "conky.h"
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <ctype.h>
14 #include <time.h>
15 #include <locale.h>
16 #include <signal.h>
17 #include <unistd.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <limits.h>
22 #if HAVE_DIRENT_H
23 #include <dirent.h>
24 #endif
25 #include <sys/time.h>
26 #include <X11/Xutil.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 #define CONFIG_FILE "$HOME/.conkyrc"
31 #define MAIL_FILE "$MAIL"
32 #define MAX_IF_BLOCK_DEPTH 5
33
34 /* alignments */
35 enum alignment {
36         TOP_LEFT = 1,
37         TOP_RIGHT,
38         BOTTOM_LEFT,
39         BOTTOM_RIGHT,
40 };
41
42
43 /* for fonts */
44 struct font_list {
45
46         char name[TEXT_BUFFER_SIZE];
47         int num;
48         XFontStruct *font;
49
50 #ifdef XFT
51         XftFont *xftfont;
52         int font_alpha;
53 #endif  
54
55 };
56 static int selected_font = 0;
57 static int font_count = -1;
58 struct font_list *fonts = NULL;
59
60 #ifdef XFT
61
62 #define font_height() use_xft ? (fonts[selected_font].xftfont->ascent + fonts[selected_font].xftfont->descent) : \
63 (fonts[selected_font].font->max_bounds.ascent + fonts[selected_font].font->max_bounds.descent)
64 #define font_ascent() use_xft ? fonts[selected_font].xftfont->ascent : fonts[selected_font].font->max_bounds.ascent
65 #define font_descent() use_xft ? fonts[selected_font].xftfont->descent : fonts[selected_font].font->max_bounds.descent
66
67 #else
68
69 #define font_height() (fonts[selected_font].font->max_bounds.ascent + fonts[selected_font].font->max_bounds.descent)
70 #define font_ascent() fonts[selected_font].font->max_bounds.ascent
71 #define font_descent() fonts[selected_font].font->max_bounds.descent
72
73 #endif
74
75 #define MAX_FONTS 64 // hmm, no particular reason, just makes sense.
76
77
78 static void set_font();
79
80
81 int addfont(const char *data_in)
82 {
83         if (font_count > MAX_FONTS) {
84                 CRIT_ERR("you don't need that many fonts, sorry.");
85         }
86         font_count++;
87         if (font_count == 0) {
88                 if (fonts != NULL) {
89                         free(fonts);
90                 }
91                 if ((fonts = (struct font_list*)malloc(sizeof(struct font_list))) == NULL) {
92                         CRIT_ERR("malloc");
93                 }
94         }
95         fonts = realloc(fonts, (sizeof(struct font_list) * (font_count+1)));
96         if (fonts == NULL) {
97                 CRIT_ERR("realloc in addfont");
98         }
99         if (strlen(data_in) < TEXT_BUFFER_SIZE) { // must account for null terminator
100                 strncpy(fonts[font_count].name, data_in, TEXT_BUFFER_SIZE);
101 #ifdef XFT
102                 fonts[font_count].font_alpha = 0xffff;
103 #endif
104         } else {
105                 CRIT_ERR("Oops...looks like something overflowed in addfont().");
106         }
107         return font_count;
108 }
109
110 void set_first_font(const char *data_in)
111 {
112         if (font_count < 0) {
113                 if ((fonts = (struct font_list*)malloc(sizeof(struct font_list))) == NULL) {
114                         CRIT_ERR("malloc");
115                 }
116                 font_count++;
117         }
118         if (strlen(data_in) > 1) {
119                 strncpy(fonts[0].name, data_in, TEXT_BUFFER_SIZE-1);
120 #ifdef XFT
121                 fonts[0].font_alpha = 0xffff;
122 #endif
123         }
124 }
125
126 void free_fonts()
127 {
128         int i;
129         for (i=0;i<=font_count;i++) {
130 #ifdef XFT
131                 if (use_xft) {
132                         XftFontClose(display, fonts[i].xftfont);
133                 } else
134 #endif
135                 {
136                         XFreeFont(display, fonts[i].font);
137                 }
138 }
139         free(fonts);
140         fonts = NULL;
141         font_count = -1;
142         selected_font = 0;
143         set_first_font("6x10");
144 }
145
146
147 static void load_fonts()
148 {
149         int i;
150         for (i=0;i<=font_count;i++) {
151 #ifdef XFT
152         /* load Xft font */
153         if (use_xft) {
154         /*if (fonts[i].xftfont != NULL && selected_font == 0) {
155                         XftFontClose(display, fonts[i].xftfont);
156         }*/
157                 if ((fonts[i].xftfont =
158                         XftFontOpenName(display, screen, fonts[i].name)) != NULL)
159                         continue;
160                 
161                 ERR("can't load Xft font '%s'", fonts[i].name);
162                 if ((fonts[i].xftfont =
163                         XftFontOpenName(display, screen,
164                                         "courier-12")) != NULL)
165                         continue;
166                 
167                 ERR("can't load Xft font '%s'", "courier-12");
168                 
169                 if ((fonts[i].font = XLoadQueryFont(display, "fixed")) == NULL) {
170                         CRIT_ERR("can't load font '%s'", "fixed");
171                 }
172                 use_xft = 0;
173                 
174                 continue;
175         }
176 #endif
177         /* load normal font */
178 /*      if (fonts[i].font != NULL)
179                 XFreeFont(display, fonts[i].font);*/
180         
181         if ((fonts[i].font = XLoadQueryFont(display, fonts[i].name)) == NULL) {
182                 ERR("can't load font '%s'", fonts[i].name);
183                 if ((fonts[i].font = XLoadQueryFont(display, "fixed")) == NULL) {
184                         CRIT_ERR("can't load font '%s'", "fixed");
185                 }
186         }
187         }
188 }
189
190 /* default config file */
191 static char *current_config;
192
193 /* set to 1 if you want all text to be in uppercase */
194 static unsigned int stuff_in_upper_case;
195
196 /* Position on the screen */
197 static int text_alignment;
198 static int gap_x, gap_y;
199
200 /* Update interval */
201 static double update_interval;
202
203 /* Run how many times? */
204 static unsigned long total_run_times;
205
206 /* fork? */
207 static int fork_to_background;
208
209 /* border */
210 static int draw_borders;
211 static int stippled_borders;
212
213 static int draw_shades, draw_outline;
214
215 static int border_margin, border_width;
216
217 static long default_fg_color, default_bg_color, default_out_color;
218
219 static int cpu_avg_samples, net_avg_samples;
220
221 /* create own window or draw stuff to root? */
222 static int own_window = 0;
223
224 #ifdef OWN_WINDOW
225 /* fixed size/pos is set if wm/user changes them */
226 static int fixed_size = 0, fixed_pos = 0;
227 #endif
228
229 static int minimum_width, minimum_height;
230
231 /* no buffers in used memory? */
232 int no_buffers;
233
234 /* pad percentages to decimals? */
235 static int pad_percents = 0;
236
237 /* UTF-8 */
238 int utf8_mode = 0;
239
240
241 /* Text that is shown */
242 static char original_text[] =
243     "$nodename - $sysname $kernel on $machine\n"
244     "$hr\n"
245     "${color grey}Uptime:$color $uptime\n"
246     "${color grey}Frequency (in MHz):$color $freq\n"
247     "${color grey}RAM Usage:$color $mem/$memmax - $memperc% ${membar 4}\n"
248     "${color grey}Swap Usage:$color $swap/$swapmax - $swapperc% ${swapbar 4}\n"
249     "${color grey}CPU Usage:$color $cpu% ${cpubar 4}\n"
250     "${color grey}Processes:$color $processes  ${color grey}Running:$color $running_processes\n"
251     "$hr\n"
252     "${color grey}File systems:\n"
253     " / $color${fs_free /}/${fs_size /} ${fs_bar 6 /}\n"
254     "${color grey}Networking:\n"
255     " Up:$color ${upspeed eth0} k/s${color grey} - Down:$color ${downspeed eth0} k/s\n"
256     "${color grey}Temperatures:\n"
257     " CPU:$color ${i2c temp 1}°C${color grey} - MB:$color ${i2c temp 2}°C\n"
258     "$hr\n"
259 #ifdef SETI
260     "${color grey}SETI@Home Statistics:\n"
261     "${color grey}Seti Unit Number:$color $seti_credit\n"
262     "${color grey}Seti Progress:$color $seti_prog% $seti_progbar\n"
263 #endif
264 #ifdef MPD
265     "${color grey}MPD: $mpd_status $mpd_artist - $mpd_title from $mpd_album at $mpd_vol\n"
266     "Bitrate: $mpd_bitrate\n" "Progress: $mpd_bar\n"
267 #endif
268     "${color grey}Name          PID     CPU%    MEM%\n"
269     " ${color lightgrey} ${top name 1} ${top pid 1} ${top cpu 1} ${top mem 1}\n"
270     " ${color lightgrey} ${top name 2} ${top pid 2} ${top cpu 2} ${top mem 2}\n"
271     " ${color lightgrey} ${top name 3} ${top pid 3} ${top cpu 3} ${top mem 3}\n"
272     " ${color lightgrey} ${top name 4} ${top pid 4} ${top cpu 4} ${top mem 4}\n"
273     "${tail /var/log/Xorg.0.log 3}";
274
275 static char *text = original_text;
276
277 static int total_updates;
278
279 /* if-blocks */
280 static int blockdepth = 0;
281 static int if_jumped = 0;
282 static int blockstart[MAX_IF_BLOCK_DEPTH];
283
284 int check_mount(char *s)
285 {
286         int ret = 0;
287         FILE *mtab = fopen("/etc/mtab", "r");
288         if (mtab) {
289                 char buf1[256], buf2[128];
290                 while (fgets(buf1, 256, mtab)) {
291                         sscanf(buf1, "%*s %128s", buf2);
292                         if (!strcmp(s, buf2)) {
293                                 ret = 1;
294                                 break;
295                         }
296                 }
297                 fclose(mtab);
298         } else {
299                 ERR("Could not open mtab");
300         }
301         return ret;
302 }
303
304
305
306 static inline int calc_text_width(const char *s, unsigned int l)
307 {
308 #ifdef XFT
309         if (use_xft) {
310                 XGlyphInfo gi;
311                 if (utf8_mode) {
312                         XftTextExtentsUtf8(display, fonts[selected_font].xftfont, s, l, &gi);
313                 } else {
314                         XftTextExtents8(display, fonts[selected_font].xftfont, s, l, &gi);
315                 }
316                 return gi.xOff;
317         } else
318 #endif
319         {
320                 return XTextWidth(fonts[selected_font].font, s, l);
321         }
322 }
323
324 /* formatted text to render on screen, generated in generate_text(),
325  * drawn in draw_stuff() */
326
327 static char text_buffer[TEXT_BUFFER_SIZE * 4];
328
329 /* special stuff in text_buffer */
330
331 #define SPECIAL_CHAR '\x01'
332
333 enum {
334         HORIZONTAL_LINE,
335         STIPPLED_HR,
336         BAR,
337         FG,
338         BG,
339         OUTLINE,
340         ALIGNR,
341         ALIGNC,
342         GRAPH,
343         OFFSET,
344         FONT,
345 };
346
347 static struct special_t {
348         int type;
349         short height;
350         short width;
351         long arg;
352         double *graph;
353         double graph_scale;
354         int graph_width;
355         int scaled;
356         short font_added;
357         unsigned long first_colour; // for graph gradient
358         unsigned long last_colour;
359 } specials[128];
360
361 static int special_count;
362 static int special_index;       /* used when drawing */
363
364 #define MAX_GRAPH_DEPTH 256     /* why 256? who knows. */
365
366 static struct special_t *new_special(char *buf, int t)
367 {
368         if (special_count >= 128)
369                 CRIT_ERR("too many special things in text");
370
371         buf[0] = SPECIAL_CHAR;
372         buf[1] = '\0';
373         if (t == GRAPH && specials[special_count].graph == NULL) {
374                 if (specials[special_count].width > 0
375                     && specials[special_count].width < MAX_GRAPH_DEPTH)
376                         specials[special_count].graph_width = specials[special_count].width - 3;        // subtract 3 for the box
377                 else
378                         specials[special_count].graph_width =
379                             MAX_GRAPH_DEPTH;
380                 specials[special_count].graph =
381                     calloc(specials[special_count].graph_width,
382                            sizeof(double));
383                 specials[special_count].graph_scale = 100;
384         }
385         specials[special_count].type = t;
386         return &specials[special_count++];
387 }
388
389 typedef struct tailstring_list {
390         char data[TEXT_BUFFER_SIZE];
391         struct tailstring_list *next;
392 } tailstring;
393
394 void addtail(tailstring ** head, char *data_in)
395 {
396         tailstring *tmp;
397         if ((tmp = malloc(sizeof(*tmp))) == NULL) {
398                 CRIT_ERR("malloc");
399         }
400         strncpy(tmp->data, data_in, TEXT_BUFFER_SIZE);
401         tmp->next = *head;
402         *head = tmp;
403 }
404
405 void freetail(tailstring * head)
406 {
407         tailstring *tmp;
408
409         while (head != NULL) {
410                 tmp = head->next;
411                 free(head);
412                 head = tmp;
413         }
414 }
415
416
417
418 static void new_bar(char *buf, int w, int h, int usage)
419 {
420         struct special_t *s = new_special(buf, BAR);
421         s->arg = (usage > 255) ? 255 : ((usage < 0) ? 0 : usage);
422         s->width = w;
423         s->height = h;
424 }
425
426 static const char *scan_bar(const char *args, int *w, int *h)
427 {
428         *w = 0;                 /* zero width means all space that is available */
429         *h = 6;
430         /* bar's argument is either height or height,width */
431         if (args) {
432                 int n = 0;
433                 if (sscanf(args, "%d,%d %n", h, w, &n) <= 1)
434                         sscanf(args, "%d %n", h, &n);
435                 args += n;
436         }
437
438         return args;
439 }
440
441 static char *scan_font(const char *args)
442 {
443         if (args && sizeof(args) < 127) {
444                 return strdup(args);
445         }
446         else {
447                 ERR("font scan failed, lets hope it doesn't mess stuff up");
448         }
449         return NULL;
450 }
451
452 static void new_font(char *buf, char * args) {
453         struct special_t *s = new_special(buf, FONT);
454         if (!s->font_added || strcmp(args, fonts[s->font_added].name)) {
455                 int tmp = selected_font;
456                 selected_font = s->font_added = addfont(args);
457                 load_fonts();
458                 set_font();
459                 selected_font = tmp;
460         }
461 }
462
463 inline void graph_append(struct special_t *graph, double f)
464 {
465         int i;
466         if (graph->scaled) {
467                 graph->graph_scale = 0;
468         }
469         graph->graph[graph->graph_width - 1] = f; /* add new data */
470         for (i = 0; i < graph->graph_width - 1; i++) { /* shift all the data by 1 */
471                 graph->graph[i] = graph->graph[i + 1];
472                 if (graph->scaled && graph->graph[i] > graph->graph_scale) {
473                         graph->graph_scale = graph->graph[i]; /* check if we need to update the scale */
474                 }
475         }
476 }
477
478 static void new_graph(char *buf, int w, int h, unsigned int first_colour, unsigned int second_colour, double i, int scaled)
479 {
480         struct special_t *s = new_special(buf, GRAPH);
481         s->width = w;
482         s->height = h;
483         s->first_colour = first_colour;
484         s->last_colour = second_colour;
485         s->scaled = scaled;
486         if (s->width) {
487                 s->graph_width = s->width - 3;  // subtract 3 for rectangle around
488         }
489         if (scaled) {
490                 s->graph_scale = 1;
491         } else {
492                 s->graph_scale = 100;
493         }
494         graph_append(s, i);
495 }
496
497 static const char *scan_graph(const char *args, int *w, int *h, unsigned int *first_colour, unsigned int *last_colour)
498 {
499         *w = 0;                 /* zero width means all space that is available */
500         *h = 25;
501         *first_colour = 0;
502         *last_colour = 0;
503         /* graph's argument is either height or height,width */
504         if (args) {
505                 if (sscanf(args, "%*s %d,%d %x %x", h, w, first_colour, last_colour) < 4) {
506                         if (sscanf(args, "%d,%d %x %x", h, w, first_colour, last_colour) < 4) {
507                                 *w = 0;
508                                 *h = 25;                        
509                                 if (sscanf(args, "%*s %x %x", first_colour, last_colour) < 2) {
510                                 *w = 0;
511                                 *h = 25;
512                                 if (sscanf(args, "%x %x", first_colour, last_colour) < 2) {
513                                         *first_colour = 0;
514                                         *last_colour = 0;
515                                         if (sscanf(args, "%d,%d", h, w) < 2) {
516                                                 *first_colour = 0;
517                                                 *last_colour = 0;
518                                                 sscanf(args, "%*s %d,%d", h, w);
519                                         }
520                                 }
521                         }
522                         }
523                 }
524         }
525
526         return args;
527 }
528
529
530 static inline void new_hr(char *buf, int a)
531 {
532         new_special(buf, HORIZONTAL_LINE)->height = a;
533 }
534
535 static inline void new_stippled_hr(char *buf, int a, int b)
536 {
537         struct special_t *s = new_special(buf, STIPPLED_HR);
538         s->height = b;
539         s->arg = a;
540 }
541
542 static inline void new_fg(char *buf, long c)
543 {
544         new_special(buf, FG)->arg = c;
545 }
546
547 static inline void new_bg(char *buf, long c)
548 {
549         new_special(buf, BG)->arg = c;
550 }
551
552 static inline void new_outline(char *buf, long c)
553 {
554         new_special(buf, OUTLINE)->arg = c;
555 }
556
557 static inline void new_offset(char *buf, long c)
558 {
559        new_special(buf, OFFSET)->arg = c;
560 }
561
562 static inline void new_alignr(char *buf, long c)
563 {
564         new_special(buf, ALIGNR)->arg = c;
565 }
566
567 static inline void new_alignc(char *buf, long c)
568 {
569         new_special(buf, ALIGNC)->arg = c;
570 }
571
572 /* quite boring functions */
573
574 static inline void for_each_line(char *b, void (*f) (char *))
575 {
576         char *ps, *pe;
577
578         for (ps = b, pe = b; *pe; pe++) {
579                 if (*pe == '\n') {
580                         *pe = '\0';
581                         f(ps);
582                         *pe = '\n';
583                         ps = pe + 1;
584                 }
585         }
586
587         if (ps < pe)
588                 f(ps);
589 }
590
591 static void convert_escapes(char *buf)
592 {
593         char *p = buf, *s = buf;
594
595         while (*s) {
596                 if (*s == '\\') {
597                         s++;
598                         if (*s == 'n')
599                                 *p++ = '\n';
600                         else if (*s == '\\')
601                                 *p++ = '\\';
602                         s++;
603                 } else
604                         *p++ = *s++;
605         }
606         *p = '\0';
607 }
608
609 /* converts from bytes to human readable format (k, M, G) */
610 static void human_readable(long long a, char *buf, int size)
611 {
612         if (a >= 1024 * 1024 * 1024)
613                 snprintf(buf, size, "%.2fG", (a / 1024 / 1024) / 1024.0);
614         else if (a >= 1024 * 1024) {
615                 double m = (a / 1024) / 1024.0;
616                 if (m >= 100.0)
617                         snprintf(buf, size, "%.0fM", m);
618                 else
619                         snprintf(buf, size, "%.1fM", m);
620         } else if (a >= 1024)
621                 snprintf(buf, size, "%Ldk", a / (long long) 1024);
622         else
623                 snprintf(buf, size, "%Ld", a);
624 }
625
626 /* text handling */
627
628 enum text_object_type {
629         OBJ_acpiacadapter,
630         OBJ_adt746xcpu,
631         OBJ_adt746xfan,
632         OBJ_acpifan,
633         OBJ_addr,
634         OBJ_linkstatus,
635         OBJ_acpitemp,
636         OBJ_battery,
637         OBJ_buffers,
638         OBJ_cached,
639         OBJ_color,
640         OBJ_font,
641         OBJ_cpu,
642         OBJ_cpubar,
643         OBJ_cpugraph,
644         OBJ_downspeed,
645         OBJ_downspeedf,
646         OBJ_downspeedgraph,
647         OBJ_else,
648         OBJ_endif,
649         OBJ_exec,
650         OBJ_execi,
651         OBJ_execbar,
652         OBJ_execgraph,
653         OBJ_freq,
654         OBJ_fs_bar,
655         OBJ_fs_bar_free,
656         OBJ_fs_free,
657         OBJ_fs_free_perc,
658         OBJ_fs_size,
659         OBJ_fs_used,
660         OBJ_fs_used_perc,
661         OBJ_hr,
662         OBJ_offset,
663         OBJ_alignr,
664         OBJ_alignc,
665         OBJ_i2c,
666         OBJ_if_existing,
667         OBJ_if_mounted,
668         OBJ_if_running,
669         OBJ_top,
670         OBJ_top_mem,
671         OBJ_tail,
672         OBJ_kernel,
673         OBJ_loadavg,
674         OBJ_machine,
675         OBJ_mails,
676         OBJ_mem,
677         OBJ_membar,
678         OBJ_memgraph,
679         OBJ_memmax,
680         OBJ_memperc,
681         OBJ_mixer,
682         OBJ_mixerl,
683         OBJ_mixerr,
684         OBJ_mixerbar,
685         OBJ_mixerlbar,
686         OBJ_mixerrbar,
687         OBJ_new_mails,
688         OBJ_nodename,
689         OBJ_pre_exec,
690 #ifdef MLDONKEY
691         OBJ_ml_upload_counter,
692         OBJ_ml_download_counter,
693         OBJ_ml_nshared_files,
694         OBJ_ml_shared_counter,
695         OBJ_ml_tcp_upload_rate,
696         OBJ_ml_tcp_download_rate,
697         OBJ_ml_udp_upload_rate,
698         OBJ_ml_udp_download_rate,
699         OBJ_ml_ndownloaded_files,
700         OBJ_ml_ndownloading_files,
701 #endif
702         OBJ_processes,
703         OBJ_running_processes,
704         OBJ_shadecolor,
705         OBJ_outlinecolor,
706         OBJ_stippled_hr,
707         OBJ_swap,
708         OBJ_swapbar,
709         OBJ_swapmax,
710         OBJ_swapperc,
711         OBJ_sysname,
712         OBJ_temp1,              /* i2c is used instead in these */
713         OBJ_temp2,
714         OBJ_text,
715         OBJ_time,
716         OBJ_utime,
717         OBJ_totaldown,
718         OBJ_totalup,
719         OBJ_updates,
720         OBJ_upspeed,
721         OBJ_upspeedf,
722         OBJ_upspeedgraph,
723         OBJ_uptime,
724         OBJ_uptime_short,
725 #ifdef SETI
726         OBJ_seti_prog,
727         OBJ_seti_progbar,
728         OBJ_seti_credit,
729 #endif
730 #ifdef MPD
731         OBJ_mpd_title,
732         OBJ_mpd_artist,
733         OBJ_mpd_album,
734         OBJ_mpd_vol,
735         OBJ_mpd_bitrate,
736         OBJ_mpd_status,
737         OBJ_mpd_host,
738         OBJ_mpd_port,
739         OBJ_mpd_bar,
740         OBJ_mpd_elapsed,
741         OBJ_mpd_length,
742         OBJ_mpd_percent,
743 #endif
744 #ifdef METAR
745         OBJ_metar_ob_time,
746         OBJ_metar_temp,
747         OBJ_metar_tempf,
748         OBJ_metar_windchill,
749         OBJ_metar_dew_point,
750         OBJ_metar_rh,
751         OBJ_metar_windspeed,
752         OBJ_metar_winddir,
753         OBJ_metar_swinddir,
754         OBJ_metar_cloud,
755         OBJ_metar_u2d_time,
756 #endif
757 };
758
759 struct text_object {
760         int type;
761         int a, b;
762         unsigned int c, d;
763         union {
764                 char *s;        /* some string */
765                 int i;          /* some integer */
766                 long l;         /* some other integer */
767                 struct net_stat *net;
768                 struct fs_stat *fs;
769                 unsigned char loadavg[3];
770
771                 struct {
772                         struct fs_stat *fs;
773                         int w, h;
774                 } fsbar;        /* 3 */
775
776                 struct {
777                         int l;
778                         int w, h;
779                 } mixerbar;     /* 3 */
780
781                 struct {
782                         int fd;
783                         int arg;
784                         char devtype[256];
785                         char type[64];
786                 } i2c;          /* 2 */
787                 struct {
788                         int pos;
789                         char *s;
790                 } ifblock;
791                 struct {
792                         int num;
793                         int type;
794                 } top;
795
796                 struct {
797                         int wantedlines;
798                         int readlines;
799                         char *logfile;
800                         double last_update;
801                         float interval;
802                         char buffer[TEXT_BUFFER_SIZE*4];
803                 } tail;
804
805                 struct {
806                         double last_update;
807                         float interval;
808                         char *cmd;
809                         char *buffer;
810                 } execi;        /* 5 */
811
812                 struct {
813                         int a, b;
814                 } pair;         /* 2 */
815         } data;
816 };
817
818 static unsigned int text_object_count;
819 static struct text_object *text_objects;
820
821 /* new_text_object() allocates a new zeroed text_object */
822 static struct text_object *new_text_object()
823 {
824         text_object_count++;
825         text_objects = (struct text_object *) realloc(text_objects,
826                                                       sizeof(struct
827                                                              text_object) *
828                                                       text_object_count);
829         memset(&text_objects[text_object_count - 1], 0,
830                sizeof(struct text_object));
831
832         return &text_objects[text_object_count - 1];
833 }
834
835 static void free_text_objects()
836 {
837         unsigned int i;
838
839         for (i = 0; i < text_object_count; i++) {
840                 switch (text_objects[i].type) {
841                 case OBJ_acpitemp:
842                         close(text_objects[i].data.i);
843                         break;
844
845                 case OBJ_i2c:
846                         close(text_objects[i].data.i2c.fd);
847                         break;
848                 case OBJ_time:
849                 case OBJ_utime:
850                 case OBJ_if_existing:
851                 case OBJ_if_mounted:
852                 case OBJ_if_running:
853                         free(text_objects[i].data.ifblock.s);
854                         break;
855                 case OBJ_text:
856                 case OBJ_exec:
857                 case OBJ_execbar:
858                 case OBJ_execgraph:
859 #ifdef MPD
860                 case OBJ_mpd_title:
861                 case OBJ_mpd_artist:
862                 case OBJ_mpd_album:
863                 case OBJ_mpd_status:
864                 case OBJ_mpd_host:
865 #endif
866                 case OBJ_pre_exec:
867                 case OBJ_battery:
868                         free(text_objects[i].data.s);
869                         break;
870
871                 case OBJ_execi:
872                         free(text_objects[i].data.execi.cmd);
873                         free(text_objects[i].data.execi.buffer);
874                         break;
875                 }
876         }
877
878         free(text_objects);
879         text_objects = NULL;
880         text_object_count = 0;
881 }
882
883 void scan_mixer_bar(const char *arg, int *a, int *w, int *h)
884 {
885         char buf1[64];
886         int n;
887
888         if (arg && sscanf(arg, "%63s %n", buf1, &n) >= 1) {
889                 *a = mixer_init(buf1);
890                 (void) scan_bar(arg + n, w, h);
891         } else {
892                 *a = mixer_init(0);
893                 (void) scan_bar(arg, w, h);
894         }
895 }
896
897 /* construct_text_object() creates a new text_object */
898 static void construct_text_object(const char *s, const char *arg)
899 {
900         struct text_object *obj = new_text_object();
901
902 #define OBJ(a, n) if (strcmp(s, #a) == 0) { obj->type = OBJ_##a; need_mask |= (1 << n); {
903 #define END ; } } else
904
905         if (s[0] == '#') {
906                 obj->type = OBJ_color;
907                 obj->data.l = get_x11_color(s);
908         } else
909                 OBJ(acpitemp, 0) obj->data.i = open_acpi_temperature(arg);
910         END OBJ(acpiacadapter, 0)
911         END OBJ(freq, 0) END OBJ(acpifan, 0) END OBJ(battery,
912                                                      0) char bat[64];
913         if (arg)
914                 sscanf(arg, "%63s", bat);
915         else
916                 strcpy(bat, "BAT0");
917         obj->data.s = strdup(bat);
918         END OBJ(buffers, INFO_BUFFERS)
919         END OBJ(cached, INFO_BUFFERS)
920         END OBJ(cpu, INFO_CPU)
921         END OBJ(cpubar, INFO_CPU)
922          (void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
923         END OBJ(cpugraph, INFO_CPU)
924                         (void) scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d);
925         END OBJ(color, 0) obj->data.l =
926             arg ? get_x11_color(arg) : default_fg_color;
927         END
928                         OBJ(font, 0)
929                         obj->data.s = scan_font(arg);
930                         END
931                         OBJ(downspeed, INFO_NET) obj->data.net = get_net_stat(arg);
932         END OBJ(downspeedf, INFO_NET) obj->data.net = get_net_stat(arg);
933         END OBJ(downspeedgraph, INFO_NET)
934                         (void) scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d);
935         char buf[64];
936         sscanf(arg, "%63s %*i,%*i %*i", buf);
937         obj->data.net = get_net_stat(buf);
938         if (sscanf(arg, "%*s %d,%d %*d", &obj->b, &obj->a) <= 1) {
939                 if (sscanf(arg, "%*s %d,%d", &obj->b, &obj->a) <= 1) {
940                         obj->a = 0;
941                         obj->b = 25;
942                 }
943         }
944         END OBJ(
945                        else
946                        , 0)
947         if (blockdepth) {
948                 text_objects[blockstart[blockdepth - 1] -
949                              1].data.ifblock.pos = text_object_count;
950                 blockstart[blockdepth - 1] = text_object_count;
951                 obj->data.ifblock.pos = text_object_count + 2;
952         } else {
953                 ERR("$else: no matching $if_*");
954         }
955         END OBJ(endif, 0)
956         if (blockdepth) {
957                 blockdepth--;
958                 text_objects[blockstart[blockdepth] - 1].data.ifblock.pos =
959                     text_object_count;
960         } else {
961                 ERR("$endif: no matching $if_*");
962         }
963         END
964 #ifdef HAVE_POPEN
965             OBJ(exec, 0) obj->data.s = strdup(arg ? arg : "");
966         END OBJ(execbar, 0) obj->data.s = strdup(arg ? arg : "");
967         END OBJ(execgraph, 0) obj->data.s = strdup(arg ? arg : "");
968         END OBJ(execi, 0) unsigned int n;
969
970         if (!arg
971             || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
972                 char buf[256];
973                 ERR("${execi <interval> command}");
974                 obj->type = OBJ_text;
975                 snprintf(buf, 256, "${%s}", s);
976                 obj->data.s = strdup(buf);
977         } else {
978                 obj->data.execi.cmd = strdup(arg + n);
979                 obj->data.execi.buffer =
980                     (char *) calloc(1, TEXT_BUFFER_SIZE);
981         }
982         END OBJ(pre_exec, 0) obj->type = OBJ_text;
983         if (arg) {
984                 FILE *fp = popen(arg, "r");
985                 unsigned int n;
986                 char buf[2048];
987
988                 n = fread(buf, 1, 2048, fp);
989                 buf[n] = '\0';
990
991                 if (n && buf[n - 1] == '\n')
992                         buf[n - 1] = '\0';
993
994                 (void) pclose(fp);
995
996                 obj->data.s = strdup(buf);
997         } else
998                 obj->data.s = strdup("");
999         END
1000 #endif
1001             OBJ(fs_bar, INFO_FS) obj->data.fsbar.h = 4;
1002         arg = scan_bar(arg, &obj->data.fsbar.w, &obj->data.fsbar.h);
1003         if (arg) {
1004                 while (isspace(*arg))
1005                         arg++;
1006                 if (*arg == '\0')
1007                         arg = "/";
1008         } else
1009                 arg = "/";
1010         obj->data.fsbar.fs = prepare_fs_stat(arg);
1011         END OBJ(fs_bar_free, INFO_FS) obj->data.fsbar.h = 4;
1012         if (arg) {
1013                 unsigned int n;
1014                 if (sscanf(arg, "%d %n", &obj->data.fsbar.h, &n) >= 1)
1015                         arg += n;
1016         } else
1017                 arg = "/";
1018         obj->data.fsbar.fs = prepare_fs_stat(arg);
1019         END OBJ(fs_free, INFO_FS) if (!arg)
1020                  arg = "/";
1021         obj->data.fs = prepare_fs_stat(arg);
1022         END OBJ(fs_used_perc, INFO_FS) if (!arg)
1023                  arg = "/";
1024         obj->data.fs = prepare_fs_stat(arg);
1025         END OBJ(fs_free_perc, INFO_FS) if (!arg)
1026                  arg = "/";
1027         obj->data.fs = prepare_fs_stat(arg);
1028         END OBJ(fs_size, INFO_FS) if (!arg)
1029                  arg = "/";
1030         obj->data.fs = prepare_fs_stat(arg);
1031         END OBJ(fs_used, INFO_FS) if (!arg)
1032                  arg = "/";
1033         obj->data.fs = prepare_fs_stat(arg);
1034         END OBJ(hr, 0) obj->data.i = arg ? atoi(arg) : 1;
1035         END OBJ(offset, 0) obj->data.i = arg ? atoi(arg) : 1;
1036         END OBJ(i2c, INFO_I2C) char buf1[64], buf2[64];
1037         int n;
1038
1039         if (!arg) {
1040                 ERR("i2c needs arguments");
1041                 obj->type = OBJ_text;
1042                 obj->data.s = strdup("${i2c}");
1043                 return;
1044         }
1045
1046         if (sscanf(arg, "%63s %63s %d", buf1, buf2, &n) != 3) {
1047                 /* if scanf couldn't read three values, read type and num and use
1048                  * default device */
1049                 sscanf(arg, "%63s %d", buf2, &n);
1050                 obj->data.i2c.fd =
1051                     open_i2c_sensor(0, buf2, n, &obj->data.i2c.arg,
1052                                     obj->data.i2c.devtype);
1053                 strcpy(obj->data.i2c.type, buf2);
1054         } else {
1055                 obj->data.i2c.fd =
1056                     open_i2c_sensor(buf1, buf2, n, &obj->data.i2c.arg,
1057                                     obj->data.i2c.devtype);
1058                 strcpy(obj->data.i2c.type, buf2);
1059         }
1060
1061         END OBJ(top, INFO_TOP)
1062         char buf[64];
1063         int n;
1064         if (!arg) {
1065                 ERR("top needs arguments");
1066                 obj->type = OBJ_text;
1067                 obj->data.s = strdup("${top}");
1068                 return;
1069         }
1070         if (sscanf(arg, "%63s %i", buf, &n) == 2) {
1071                 if (strcmp(buf, "name") == 0) {
1072                         obj->data.top.type = TOP_NAME;
1073                 } else if (strcmp(buf, "cpu") == 0) {
1074                         obj->data.top.type = TOP_CPU;
1075                 } else if (strcmp(buf, "pid") == 0) {
1076                         obj->data.top.type = TOP_PID;
1077                 } else if (strcmp(buf, "mem") == 0) {
1078                         obj->data.top.type = TOP_MEM;
1079                 } else {
1080                         ERR("invalid arg for top");
1081                         return;
1082                 }
1083                 if (n < 1 || n > 10) {
1084                         CRIT_ERR("invalid arg for top");
1085                         return;
1086                 } else {
1087                         obj->data.top.num = n - 1;
1088                         top_cpu = 1;
1089                 }
1090         } else {
1091                 ERR("invalid args given for top");
1092                 return;
1093         }
1094         END OBJ(top_mem, INFO_TOP)
1095         char buf[64];
1096         int n;
1097         if (!arg) {
1098                 ERR("top_mem needs arguments");
1099                 obj->type = OBJ_text;
1100                 obj->data.s = strdup("${top_mem}");
1101                 return;
1102         }
1103         if (sscanf(arg, "%63s %i", buf, &n) == 2) {
1104                 if (strcmp(buf, "name") == 0) {
1105                         obj->data.top.type = TOP_NAME;
1106                 } else if (strcmp(buf, "cpu") == 0) {
1107                         obj->data.top.type = TOP_CPU;
1108                 } else if (strcmp(buf, "pid") == 0) {
1109                         obj->data.top.type = TOP_PID;
1110                 } else if (strcmp(buf, "mem") == 0) {
1111                         obj->data.top.type = TOP_MEM;
1112                 } else {
1113                         ERR("invalid arg for top");
1114                         return;
1115                 }
1116                 if (n < 1 || n > 10) {
1117                         CRIT_ERR("invalid arg for top");
1118                         return;
1119                 } else {
1120                         obj->data.top.num = n - 1;
1121                         top_mem = 1;
1122                 }
1123         } else {
1124                 ERR("invalid args given for top");
1125                 return;
1126         }
1127         END OBJ(addr, INFO_NET) obj->data.net = get_net_stat(arg);
1128         END OBJ(linkstatus, INFO_WIFI) obj->data.net = get_net_stat(arg);
1129         END OBJ(tail, 0)
1130         char buf[64];
1131         int n1, n2;
1132         if (!arg) {
1133                 ERR("tail needs arguments");
1134                 obj->type = OBJ_text;
1135                 obj->data.s = strdup("${tail}");
1136                 return;
1137         }
1138         if (sscanf(arg, "%63s %i %i", buf, &n1, &n2) == 2) {
1139                 if (n1 < 1 || n1 > 30) {
1140                         CRIT_ERR
1141                             ("invalid arg for tail, number of lines must be between 1 and 30");
1142                         return;
1143                 } else {
1144                         FILE *fp;
1145                         fp = fopen(buf, "rt");
1146                         if (fp != NULL) {
1147                                 obj->data.tail.logfile =
1148                                     malloc(TEXT_BUFFER_SIZE);
1149                                 strcpy(obj->data.tail.logfile, buf);
1150                                 obj->data.tail.wantedlines = n1 - 1;
1151                                 obj->data.tail.interval =
1152                                     update_interval * 2;
1153                                 fclose(fp);
1154                         } else {
1155                                 //fclose (fp);
1156                                 CRIT_ERR
1157                                     ("tail logfile does not exist, or you do not have correct permissions");
1158                         }
1159                 }
1160         } else if (sscanf(arg, "%63s %i %i", buf, &n1, &n2) == 3) {
1161                 if (n1 < 1 || n1 > 30) {
1162                         CRIT_ERR
1163                             ("invalid arg for tail, number of lines must be between 1 and 30");
1164                         return;
1165                 } else if (n2 < 1 || n2 < update_interval) {
1166                         CRIT_ERR
1167                             ("invalid arg for tail, interval must be greater than 0 and Conky's interval");
1168                         return;
1169                 } else {
1170                         FILE *fp;
1171                         fp = fopen(buf, "rt");
1172                         if (fp != NULL) {
1173                                 obj->data.tail.logfile =
1174                                     malloc(TEXT_BUFFER_SIZE);
1175                                 strcpy(obj->data.tail.logfile, buf);
1176                                 obj->data.tail.wantedlines = n1 - 1;
1177                                 obj->data.tail.interval = n2;
1178                                 fclose(fp);
1179                         } else {
1180                                 //fclose (fp);
1181                                 CRIT_ERR
1182                                     ("tail logfile does not exist, or you do not have correct permissions");
1183                         }
1184                 }
1185         }
1186
1187         else {
1188                 ERR("invalid args given for tail");
1189                 return;
1190         }
1191         END OBJ(loadavg, INFO_LOADAVG) int a = 1, b = 2, c = 3, r = 3;
1192         if (arg) {
1193                 r = sscanf(arg, "%d %d %d", &a, &b, &c);
1194                 if (r >= 3 && (c < 1 || c > 3))
1195                         r--;
1196                 if (r >= 2 && (b < 1 || b > 3))
1197                         r--, b = c;
1198                 if (r >= 1 && (a < 1 || a > 3))
1199                         r--, a = b, b = c;
1200         }
1201         obj->data.loadavg[0] = (r >= 1) ? (unsigned char) a : 0;
1202         obj->data.loadavg[1] = (r >= 2) ? (unsigned char) b : 0;
1203         obj->data.loadavg[2] = (r >= 3) ? (unsigned char) c : 0;
1204         END OBJ(if_existing, 0)
1205         if (blockdepth >= MAX_IF_BLOCK_DEPTH) {
1206                 CRIT_ERR("MAX_IF_BLOCK_DEPTH exceeded");
1207         }
1208         if (!arg) {
1209                 ERR("if_existing needs an argument");
1210                 obj->data.ifblock.s = 0;
1211         } else
1212                 obj->data.ifblock.s = strdup(arg);
1213         blockstart[blockdepth] = text_object_count;
1214         obj->data.ifblock.pos = text_object_count + 2;
1215         blockdepth++;
1216         END OBJ(if_mounted, 0)
1217         if (blockdepth >= MAX_IF_BLOCK_DEPTH) {
1218                 CRIT_ERR("MAX_IF_BLOCK_DEPTH exceeded");
1219         }
1220         if (!arg) {
1221                 ERR("if_mounted needs an argument");
1222                 obj->data.ifblock.s = 0;
1223         } else
1224                 obj->data.ifblock.s = strdup(arg);
1225         blockstart[blockdepth] = text_object_count;
1226         obj->data.ifblock.pos = text_object_count + 2;
1227         blockdepth++;
1228         END OBJ(if_running, 0)
1229         if (blockdepth >= MAX_IF_BLOCK_DEPTH) {
1230                 CRIT_ERR("MAX_IF_BLOCK_DEPTH exceeded");
1231         }
1232         if (arg) {
1233                 char buf[256];
1234                 snprintf(buf, 256, "pidof %s >/dev/null", arg);
1235                 obj->data.ifblock.s = strdup(buf);
1236         } else {
1237                 ERR("if_running needs an argument");
1238                 obj->data.ifblock.s = 0;
1239         }
1240         blockstart[blockdepth] = text_object_count;
1241         obj->data.ifblock.pos = text_object_count + 2;
1242         blockdepth++;
1243         END OBJ(kernel, 0)
1244         END OBJ(machine, 0)
1245         END OBJ(mails, INFO_MAIL)
1246         END OBJ(mem, INFO_MEM)
1247         END OBJ(memmax, INFO_MEM)
1248         END OBJ(memperc, INFO_MEM)
1249         END OBJ(membar, INFO_MEM)
1250          (void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
1251         END OBJ(membar, INFO_MEM)
1252                         (void) scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d);
1253         END OBJ(mixer, INFO_MIXER) obj->data.l = mixer_init(arg);
1254         END OBJ(mixerl, INFO_MIXER) obj->data.l = mixer_init(arg);
1255         END OBJ(mixerr, INFO_MIXER) obj->data.l = mixer_init(arg);
1256         END OBJ(mixerbar, INFO_MIXER)
1257             scan_mixer_bar(arg, &obj->data.mixerbar.l,
1258                            &obj->data.mixerbar.w, &obj->data.mixerbar.h);
1259         END OBJ(mixerlbar, INFO_MIXER)
1260             scan_mixer_bar(arg, &obj->data.mixerbar.l,
1261                            &obj->data.mixerbar.w, &obj->data.mixerbar.h);
1262         END OBJ(mixerrbar, INFO_MIXER)
1263             scan_mixer_bar(arg, &obj->data.mixerbar.l,
1264                            &obj->data.mixerbar.w, &obj->data.mixerbar.h);
1265         END
1266 #ifdef MLDONKEY
1267             OBJ(ml_upload_counter, INFO_MLDONKEY)
1268         END OBJ(ml_download_counter, INFO_MLDONKEY)
1269         END OBJ(ml_nshared_files, INFO_MLDONKEY)
1270         END OBJ(ml_shared_counter, INFO_MLDONKEY)
1271         END OBJ(ml_tcp_upload_rate, INFO_MLDONKEY)
1272         END OBJ(ml_tcp_download_rate, INFO_MLDONKEY)
1273         END OBJ(ml_udp_upload_rate, INFO_MLDONKEY)
1274         END OBJ(ml_udp_download_rate, INFO_MLDONKEY)
1275         END OBJ(ml_ndownloaded_files, INFO_MLDONKEY)
1276         END OBJ(ml_ndownloading_files, INFO_MLDONKEY) END
1277 #endif
1278          OBJ(new_mails, INFO_MAIL)
1279         END OBJ(nodename, 0)
1280         END OBJ(processes, INFO_PROCS)
1281         END OBJ(running_processes, INFO_RUN_PROCS)
1282         END OBJ(shadecolor, 0)
1283             obj->data.l = arg ? get_x11_color(arg) : default_bg_color;
1284         END OBJ(outlinecolor, 0)
1285             obj->data.l = arg ? get_x11_color(arg) : default_out_color;
1286         END OBJ(stippled_hr, 0) int a = stippled_borders, b = 1;
1287         if (arg) {
1288                 if (sscanf(arg, "%d %d", &a, &b) != 2)
1289                         sscanf(arg, "%d", &b);
1290         }
1291         if (a <= 0)
1292                 a = 1;
1293         obj->data.pair.a = a;
1294         obj->data.pair.b = b;
1295         END OBJ(swap, INFO_MEM)
1296         END OBJ(swapmax, INFO_MEM)
1297         END OBJ(swapperc, INFO_MEM)
1298         END OBJ(swapbar, INFO_MEM)
1299          (void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
1300         END OBJ(sysname, 0) END OBJ(temp1, INFO_I2C) obj->type = OBJ_i2c;
1301         obj->data.i2c.fd =
1302             open_i2c_sensor(0, "temp", 1, &obj->data.i2c.arg,
1303                             obj->data.i2c.devtype);
1304         END OBJ(temp2, INFO_I2C) obj->type = OBJ_i2c;
1305         obj->data.i2c.fd =
1306             open_i2c_sensor(0, "temp", 2, &obj->data.i2c.arg,
1307                             obj->data.i2c.devtype);
1308         END OBJ(time, 0) obj->data.s = strdup(arg ? arg : "%F %T");
1309         END OBJ(utime, 0) obj->data.s = strdup(arg ? arg : "%F %T");
1310         END OBJ(totaldown, INFO_NET) obj->data.net = get_net_stat(arg);
1311         END OBJ(totalup, INFO_NET) obj->data.net = get_net_stat(arg);
1312         END OBJ(updates, 0)
1313         END OBJ(alignr, 0) obj->data.i = arg ? atoi(arg) : 1;
1314         END OBJ(alignc, 0) obj->data.i = arg ? atoi(arg) : 1;
1315         END OBJ(upspeed, INFO_NET) obj->data.net = get_net_stat(arg);
1316         END OBJ(upspeedf, INFO_NET) obj->data.net = get_net_stat(arg);
1317         END OBJ(upspeedgraph, INFO_NET)
1318                         (void) scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d);
1319         char buf[64];
1320         sscanf(arg, "%63s %*i,%*i %*i", buf);
1321         obj->data.net = get_net_stat(buf);
1322         if (sscanf(arg, "%*s %d,%d %*d", &obj->b, &obj->a) <= 1) {
1323                 if (sscanf(arg, "%*s %d,%d", &obj->a, &obj->a) <= 1) {
1324                         obj->a = 0;
1325                         obj->b = 25;
1326                 }
1327         }
1328         END OBJ(uptime_short, INFO_UPTIME) END OBJ(uptime, INFO_UPTIME) END
1329             OBJ(adt746xcpu, 0) END OBJ(adt746xfan, 0) END
1330 #ifdef SETI
1331          OBJ(seti_prog, INFO_SETI) END OBJ(seti_progbar, INFO_SETI)
1332          (void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
1333         END OBJ(seti_credit, INFO_SETI) END
1334 #endif
1335 #ifdef METAR
1336          OBJ(metar_ob_time, INFO_METAR)
1337             END
1338             OBJ(metar_temp, INFO_METAR)
1339             END
1340             OBJ(metar_tempf, INFO_METAR)
1341             END
1342             OBJ(metar_windchill, INFO_METAR)
1343             END
1344             OBJ(metar_dew_point, INFO_METAR)
1345             END
1346             OBJ(metar_rh, INFO_METAR)
1347             END
1348             OBJ(metar_windspeed, INFO_METAR)
1349             END
1350             OBJ(metar_winddir, INFO_METAR)
1351             END
1352             OBJ(metar_swinddir, INFO_METAR)
1353             END OBJ(metar_cloud, INFO_METAR)
1354         END OBJ(metar_u2d_time, INFO_METAR) END
1355 #endif
1356 #ifdef MPD
1357          OBJ(mpd_artist, INFO_MPD)
1358         END OBJ(mpd_title, INFO_MPD)
1359         END OBJ(mpd_elapsed, INFO_MPD)
1360         END OBJ(mpd_length, INFO_MPD)
1361         END OBJ(mpd_percent, INFO_MPD)
1362         END OBJ(mpd_album, INFO_MPD) END OBJ(mpd_vol,
1363                                              INFO_MPD) END OBJ(mpd_bitrate,
1364                                                                INFO_MPD)
1365         END OBJ(mpd_status, INFO_MPD) END OBJ(mpd_bar, INFO_MPD)
1366          (void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
1367         END
1368 #endif
1369         {
1370                 char buf[256];
1371                 ERR("unknown variable %s", s);
1372                 obj->type = OBJ_text;
1373                 snprintf(buf, 256, "${%s}", s);
1374                 obj->data.s = strdup(buf);
1375         }
1376 #undef OBJ
1377 }
1378
1379 /* append_text() appends text to last text_object if it's text, if it isn't
1380  * it creates a new text_object */
1381 static void append_text(const char *s)
1382 {
1383         struct text_object *obj;
1384
1385         if (s == NULL || *s == '\0')
1386                 return;
1387
1388         obj = text_object_count ? &text_objects[text_object_count - 1] : 0;
1389
1390         /* create a new text object? */
1391         if (!obj || obj->type != OBJ_text) {
1392                 obj = new_text_object();
1393                 obj->type = OBJ_text;
1394                 obj->data.s = strdup(s);
1395         } else {
1396                 /* append */
1397                 obj->data.s = (char *) realloc(obj->data.s,
1398                                                strlen(obj->data.s) +
1399                                                strlen(s) + 1);
1400                 strcat(obj->data.s, s);
1401         }
1402 }
1403
1404 static void extract_variable_text(const char *p)
1405 {
1406         const char *s = p;
1407
1408         free_text_objects();
1409
1410         while (*p) {
1411                 if (*p == '$') {
1412                         *(char *) p = '\0';
1413                         append_text(s);
1414                         *(char *) p = '$';
1415                         p++;
1416                         s = p;
1417
1418                         if (*p != '$') {
1419                                 char buf[256];
1420                                 const char *var;
1421                                 unsigned int len;
1422
1423                                 /* variable is either $foo or ${foo} */
1424                                 if (*p == '{') {
1425                                         p++;
1426                                         s = p;
1427                                         while (*p && *p != '}')
1428                                                 p++;
1429                                 } else {
1430                                         s = p;
1431                                         if (*p == '#')
1432                                                 p++;
1433                                         while (*p && (isalnum((int) *p)
1434                                                       || *p == '_'))
1435                                                 p++;
1436                                 }
1437
1438                                 /* copy variable to buffer */
1439                                 len = (p - s > 255) ? 255 : (p - s);
1440                                 strncpy(buf, s, len);
1441                                 buf[len] = '\0';
1442
1443                                 if (*p == '}')
1444                                         p++;
1445                                 s = p;
1446
1447                                 var = getenv(buf);
1448
1449                                 /* if variable wasn't found from environment, use some special */
1450                                 if (!var) {
1451                                         char *p;
1452                                         char *arg = 0;
1453
1454                                         /* split arg */
1455                                         if (strchr(buf, ' ')) {
1456                                                 arg = strchr(buf, ' ');
1457                                                 *arg = '\0';
1458                                                 arg++;
1459                                                 while (isspace((int) *arg))
1460                                                         arg++;
1461                                                 if (!*arg)
1462                                                         arg = 0;
1463                                         }
1464
1465                                         /* lowercase variable name */
1466                                         p = buf;
1467                                         while (*p) {
1468                                                 *p = tolower(*p);
1469                                                 p++;
1470                                         }
1471
1472                                         construct_text_object(buf, arg);
1473                                 }
1474                                 continue;
1475                         } else
1476                                 append_text("$");
1477                 }
1478
1479                 p++;
1480         }
1481         append_text(s);
1482         if (blockdepth) {
1483                 ERR("one or more $endif's are missing");
1484         }
1485 }
1486
1487 double current_update_time, last_update_time;
1488
1489 static void generate_text()
1490 {
1491         unsigned int i, n;
1492         struct information *cur = &info;
1493         char *p;
1494
1495         special_count = 0;
1496
1497         /* update info */
1498
1499         current_update_time = get_time();
1500
1501         update_stuff(cur);
1502
1503         /* generate text */
1504
1505         n = TEXT_BUFFER_SIZE * 4 - 2;
1506         p = text_buffer;
1507
1508         for (i = 0; i < text_object_count; i++) {
1509                 struct text_object *obj = &text_objects[i];
1510
1511 #define OBJ(a) break; case OBJ_##a:
1512
1513                 switch (obj->type) {
1514                 default:
1515                         {
1516                                 ERR("not implemented obj type %d",
1517                                     obj->type);
1518                         }
1519                         OBJ(acpitemp) {
1520                                 /* does anyone have decimals in acpi temperature? */
1521                                 if (!use_spacer)
1522                                         snprintf(p, n, "%d", (int)
1523                                                  get_acpi_temperature(obj->
1524                                                                       data.
1525                                                                       i));
1526                                 else
1527                                         snprintf(p, 5, "%d    ", (int)
1528                                                  get_acpi_temperature(obj->
1529                                                                       data.
1530                                                                       i));
1531                         }
1532                         OBJ(freq) {
1533                                 snprintf(p, n, "%s", get_freq());
1534                         }
1535                         OBJ(adt746xcpu) {
1536                                 snprintf(p, n, "%s", get_adt746x_cpu());
1537                         }
1538                         OBJ(adt746xfan) {
1539                                 snprintf(p, n, "%s", get_adt746x_fan());
1540                         }
1541                         OBJ(acpifan) {
1542                                 snprintf(p, n, "%s", get_acpi_fan());
1543                         }
1544                         OBJ(acpiacadapter) {
1545                                 snprintf(p, n, "%s",
1546                                          get_acpi_ac_adapter());
1547                         }
1548                         OBJ(battery) {
1549                                 get_battery_stuff(p, n, obj->data.s);
1550                         }
1551                         OBJ(buffers) {
1552                                 human_readable(cur->buffers * 1024, p,
1553                                                255);
1554                         }
1555                         OBJ(cached) {
1556                                 human_readable(cur->cached * 1024, p, 255);
1557                         }
1558                         OBJ(cpu) {
1559                                 if (!use_spacer)
1560                                         snprintf(p, n, "%*d", pad_percents,
1561                                                  (int) (cur->cpu_usage *
1562                                                         100.0));
1563                                 else
1564                                         snprintf(p, 4, "%*d    ",
1565                                                  pad_percents,
1566                                                  (int) (cur->cpu_usage *
1567                                                         100.0));
1568                         }
1569                         OBJ(cpubar) {
1570                                 new_bar(p, obj->data.pair.a,
1571                                         obj->data.pair.b,
1572                                         (int) (cur->cpu_usage * 255.0));
1573                         }
1574                         OBJ(cpugraph) {
1575                                 new_graph(p, obj->a,
1576                                           obj->b, obj->c, obj->d,
1577                                           (unsigned int) (cur->cpu_usage *
1578                                                           100), 0);
1579                         }
1580                         OBJ(color) {
1581                                 new_fg(p, obj->data.l);
1582                         }
1583                         OBJ(font) {
1584                                 new_font(p, obj->data.s);
1585                         }
1586                         OBJ(downspeed) {
1587                                 if (!use_spacer) {
1588                                         snprintf(p, n, "%d",
1589                                                  (int) (obj->data.net->
1590                                                         recv_speed /
1591                                                         1024));
1592                                 } else
1593                                         snprintf(p, 6, "%d     ",
1594                                                  (int) (obj->data.net->
1595                                                         recv_speed /
1596                                                         1024));
1597                         }
1598                         OBJ(downspeedf) {
1599                                 if (!use_spacer)
1600                                         snprintf(p, n, "%.1f",
1601                                                  obj->data.net->
1602                                                  recv_speed / 1024.0);
1603                                 else
1604                                         snprintf(p, 8, "%.1f       ",
1605                                                  obj->data.net->
1606                                                  recv_speed / 1024.0);
1607                         }
1608                         OBJ(downspeedgraph) {
1609                                 if (obj->data.net->recv_speed == 0)     // this is just to make the ugliness at start go away
1610                                         obj->data.net->recv_speed = 0.01;
1611                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
1612                                           (obj->data.net->recv_speed /
1613                                 1024.0), 1);
1614                         }
1615                         OBJ(
1616                                    else
1617                         ) {
1618                                 if (!if_jumped) {
1619                                         i = obj->data.ifblock.pos - 2;
1620                                 } else {
1621                                         if_jumped = 0;
1622                                 }
1623                         }
1624                         OBJ(endif) {
1625                                 if_jumped = 0;
1626                         }
1627 #ifdef HAVE_POPEN
1628                         OBJ(addr) {
1629                                 snprintf(p, n, "%u.%u.%u.%u",
1630                                          obj->data.net->addr.
1631                                          sa_data[2] & 255,
1632                                          obj->data.net->addr.
1633                                          sa_data[3] & 255,
1634                                          obj->data.net->addr.
1635                                          sa_data[4] & 255,
1636                                          obj->data.net->addr.
1637                                          sa_data[5] & 255);
1638
1639                         }
1640                         OBJ(linkstatus) {
1641                                 snprintf(p, n, "%d",
1642                                          obj->data.net->linkstatus);
1643                         }
1644
1645                         OBJ(exec) {
1646                                 char *p2 = p;
1647                                 FILE *fp = popen(obj->data.s, "r");
1648                                 int n2 = fread(p, 1, n, fp);
1649                                 (void) pclose(fp);
1650
1651                                 p[n2] = '\0';
1652                                 if (n2 && p[n2 - 1] == '\n')
1653                                         p[n2 - 1] = '\0';
1654
1655                                 while (*p2) {
1656                                         if (*p2 == '\001')
1657                                                 *p2 = ' ';
1658                                         p2++;
1659                                 }
1660                         }
1661                         OBJ(execbar) {
1662                                 char *p2 = p;
1663                                 FILE *fp = popen(obj->data.s, "r");
1664                                 int n2 = fread(p, 1, n, fp);
1665                                 (void) pclose(fp);
1666
1667                                 p[n2] = '\0';
1668                                 if (n2 && p[n2 - 1] == '\n')
1669                                         p[n2 - 1] = '\0';
1670
1671                                 while (*p2) {
1672                                         if (*p2 == '\001')
1673                                                 *p2 = ' ';
1674                                         p2++;
1675                                 }
1676                                 double barnum;
1677                                 if (sscanf(p, "%lf", &barnum) == 0) {
1678                                         ERR("reading execbar value failed (perhaps it's not the correct format?)");
1679                                 }
1680                                 if (barnum > 100 || barnum < 0) {
1681                                         ERR("your execbar value is not between 0 and 100, therefore it will be ignored");
1682                                 } else {
1683                                         barnum = barnum / 100.0;
1684                                         new_bar(p, 0,
1685                                                 4, (int) (barnum * 255.0));
1686                                 }
1687
1688                         }
1689                         OBJ(execgraph) {
1690                                 char *p2 = p;
1691                                 FILE *fp = popen(obj->data.s, "r");
1692                                 int n2 = fread(p, 1, n, fp);
1693                                 (void) pclose(fp);
1694
1695                                 p[n2] = '\0';
1696                                 if (n2 && p[n2 - 1] == '\n')
1697                                         p[n2 - 1] = '\0';
1698
1699                                 while (*p2) {
1700                                         if (*p2 == '\001')
1701                                                 *p2 = ' ';
1702                                         p2++;
1703                                 }
1704                                 double barnum;
1705                                 if (sscanf(p, "%lf", &barnum) == 0) {
1706                                         ERR("reading execgraph value failed (perhaps it's not the correct format?)");
1707                                 }
1708                                 if (barnum > 100 || barnum < 0) {
1709                                         ERR("your execgraph value is not between 0 and 100, therefore it will be ignored");
1710                                 } else {
1711                                         new_graph(p, 0,
1712                                         25, obj->c, obj->d, (int) (barnum), 0);
1713                                 }
1714
1715                         }
1716                         OBJ(execi) {
1717                                 if (current_update_time -
1718                                     obj->data.execi.last_update <
1719                                     obj->data.execi.interval) {
1720                                         snprintf(p, n, "%s",
1721                                                  obj->data.execi.buffer);
1722                                 } else {
1723                                         char *p2 = obj->data.execi.buffer;
1724                                         FILE *fp =
1725                                             popen(obj->data.execi.cmd,
1726                                                   "r");
1727                                         int n2 =
1728                                             fread(p2, 1, TEXT_BUFFER_SIZE,
1729                                                   fp);
1730                                         (void) pclose(fp);
1731
1732                                         p2[n2] = '\0';
1733                                         if (n2 && p2[n2 - 1] == '\n')
1734                                                 p2[n2 - 1] = '\0';
1735
1736                                         while (*p2) {
1737                                                 if (*p2 == '\001')
1738                                                         *p2 = ' ';
1739                                                 p2++;
1740                                         }
1741
1742                                         snprintf(p, n, "%s",
1743                                                  obj->data.execi.buffer);
1744
1745                                         obj->data.execi.last_update =
1746                                             current_update_time;
1747                                 }
1748                         }
1749 #endif
1750                         OBJ(fs_bar) {
1751                                 if (obj->data.fs != NULL) {
1752                                         if (obj->data.fs->size == 0)
1753                                                 new_bar(p,
1754                                                         obj->data.fsbar.w,
1755                                                         obj->data.fsbar.h,
1756                                                         255);
1757                                         else
1758                                                 new_bar(p,
1759                                                         obj->data.fsbar.w,
1760                                                         obj->data.fsbar.h,
1761                                                         (int) (255 -
1762                                                                obj->data.
1763                                                                fsbar.fs->
1764                                                                avail *
1765                                                                255 /
1766                                                                obj->data.
1767                                                                fs->size));
1768                                 }
1769                         }
1770                         OBJ(fs_free) {
1771                                 if (obj->data.fs != NULL)
1772                                         human_readable(obj->data.fs->avail,
1773                                                        p, 255);
1774                         }
1775                         OBJ(fs_free_perc) {
1776                                 if (obj->data.fs != NULL) {
1777                                         if (obj->data.fs->size)
1778                                                 snprintf(p, n, "%*d",
1779                                                          pad_percents,
1780                                                          (int) ((obj->data.
1781                                                                  fs->
1782                                                                  avail *
1783                                                                  100) /
1784                                                                 obj->data.
1785                                                                 fs->size));
1786                                         else
1787                                                 snprintf(p, n, "0");
1788                                 }
1789                         }
1790                         OBJ(fs_size) {
1791                                 if (obj->data.fs != NULL)
1792                                         human_readable(obj->data.fs->size,
1793                                                        p, 255);
1794                         }
1795                         OBJ(fs_used) {
1796                                 if (obj->data.fs != NULL)
1797                                         human_readable(obj->data.fs->size -
1798                                                        obj->data.fs->avail,
1799                                                        p, 255);
1800                         }
1801                         OBJ(fs_bar_free) {
1802                                 if (obj->data.fs != NULL) {
1803                                         if (obj->data.fs->size == 0)
1804                                                 new_bar(p,
1805                                                         obj->data.fsbar.w,
1806                                                         obj->data.fsbar.h,
1807                                                         255);
1808                                         else
1809                                                 new_bar(p,
1810                                                         obj->data.fsbar.w,
1811                                                         obj->data.fsbar.h,
1812                                                         (int) (obj->data.
1813                                                                fsbar.fs->
1814                                                                avail *
1815                                                                255 /
1816                                                                obj->data.
1817                                                                fs->size));
1818                                 }
1819                         }
1820                         OBJ(fs_used_perc) {
1821                                 if (obj->data.fs != NULL) {
1822                                         if (obj->data.fs->size)
1823                                                 snprintf(p, 4, "%d",
1824                                                          100 - ((int)
1825                                                                 ((obj->
1826                                                                   data.fs->
1827                                                                   avail *
1828                                                                   100) /
1829                                                                  obj->data.
1830                                                                  fs->
1831                                                                  size)));
1832                                         else
1833                                                 snprintf(p, n, "0");
1834                                 }
1835                         }
1836                         OBJ(loadavg) {
1837                                 float *v = info.loadavg;
1838
1839                                 if (obj->data.loadavg[2])
1840                                         snprintf(p, n, "%.2f %.2f %.2f",
1841                                                  v[obj->data.loadavg[0] -
1842                                                    1],
1843                                                  v[obj->data.loadavg[1] -
1844                                                    1],
1845                                                  v[obj->data.loadavg[2] -
1846                                                    1]);
1847                                 else if (obj->data.loadavg[1])
1848                                         snprintf(p, n, "%.2f %.2f",
1849                                                  v[obj->data.loadavg[0] -
1850                                                    1],
1851                                                  v[obj->data.loadavg[1] -
1852                                                    1]);
1853                                 else if (obj->data.loadavg[0])
1854                                         snprintf(p, n, "%.2f",
1855                                                  v[obj->data.loadavg[0] -
1856                                                    1]);
1857                         }
1858                         OBJ(hr) {
1859                                 new_hr(p, obj->data.i);
1860                         }
1861                         OBJ(offset) {
1862                                 new_offset(p, obj->data.i);
1863                         }
1864                         OBJ(i2c) {
1865                                 double r;
1866
1867                                 r = get_i2c_info(&obj->data.i2c.fd,
1868                                                  obj->data.i2c.arg,
1869                                                  obj->data.i2c.devtype,
1870                                                  obj->data.i2c.type);
1871
1872                                 if (r >= 100.0 || r == 0)
1873                                         snprintf(p, n, "%d", (int) r);
1874                                 else
1875                                         snprintf(p, n, "%.1f", r);
1876                         }
1877                         OBJ(alignr) {
1878                                 new_alignr(p, obj->data.i);
1879                         }
1880                         OBJ(alignc) {
1881                                 new_alignc(p, obj->data.i);
1882                         }
1883                         OBJ(if_existing) {
1884                                 struct stat tmp;
1885                                 if ((obj->data.ifblock.s)
1886                                     && (stat(obj->data.ifblock.s, &tmp) ==
1887                                         -1)) {
1888                                         i = obj->data.ifblock.pos - 2;
1889                                         if_jumped = 1;
1890                                 } else
1891                                         if_jumped = 0;
1892                         }
1893                         OBJ(if_mounted) {
1894                                 if ((obj->data.ifblock.s)
1895                                     && (!check_mount(obj->data.ifblock.s))) {
1896                                         i = obj->data.ifblock.pos - 2;
1897                                         if_jumped = 1;
1898                                 } else
1899                                         if_jumped = 0;
1900                         }
1901                         OBJ(if_running) {
1902                                 if ((obj->data.ifblock.s)
1903                                     && system(obj->data.ifblock.s)) {
1904                                         i = obj->data.ifblock.pos - 2;
1905                                         if_jumped = 1;
1906                                 } else
1907                                         if_jumped = 0;
1908                         }
1909                         OBJ(kernel) {
1910                                 snprintf(p, n, "%s", cur->uname_s.release);
1911                         }
1912                         OBJ(machine) {
1913                                 snprintf(p, n, "%s", cur->uname_s.machine);
1914                         }
1915
1916                         /* memory stuff */
1917                         OBJ(mem) {
1918                                 human_readable(cur->mem * 1024, p, 6);
1919                         }
1920                         OBJ(memmax) {
1921                                 human_readable(cur->memmax * 1024, p, 255);
1922                         }
1923                         OBJ(memperc) {
1924                                 if (cur->memmax) {
1925                                         if (!use_spacer)
1926                                                 snprintf(p, n, "%*d",
1927                                                          pad_percents,
1928                                                          (cur->mem * 100) /
1929                                                          (cur->memmax));
1930                                         else
1931                                                 snprintf(p, 4, "%*d   ",
1932                                                          pad_percents,
1933                                                          (cur->mem * 100) /
1934                                                          (cur->memmax));
1935                                 }
1936                         }
1937                         OBJ(membar) {
1938                                 new_bar(p, obj->data.pair.a,
1939                                         obj->data.pair.b,
1940                                         cur->memmax ? (cur->mem * 255) /
1941                                         (cur->memmax) : 0);
1942                         }
1943
1944                         OBJ(memgraph) {
1945                                 new_graph(p, obj->a,
1946                                           obj->b, obj->c, obj->d,
1947                                           cur->memmax ? (cur->mem) /
1948                                 (cur->memmax) : 0, 0);
1949                         }
1950                         /* mixer stuff */
1951                         OBJ(mixer) {
1952                                 snprintf(p, n, "%d",
1953                                          mixer_get_avg(obj->data.l));
1954                         }
1955                         OBJ(mixerl) {
1956                                 snprintf(p, n, "%d",
1957                                          mixer_get_left(obj->data.l));
1958                         }
1959                         OBJ(mixerr) {
1960                                 snprintf(p, n, "%d",
1961                                          mixer_get_right(obj->data.l));
1962                         }
1963                         OBJ(mixerbar) {
1964                                 new_bar(p, obj->data.mixerbar.w,
1965                                         obj->data.mixerbar.h,
1966                                         mixer_get_avg(obj->data.mixerbar.
1967                                                       l) * 255 / 100);
1968                         }
1969                         OBJ(mixerlbar) {
1970                                 new_bar(p, obj->data.mixerbar.w,
1971                                         obj->data.mixerbar.h,
1972                                         mixer_get_left(obj->data.mixerbar.
1973                                                        l) * 255 / 100);
1974                         }
1975                         OBJ(mixerrbar) {
1976                                 new_bar(p, obj->data.mixerbar.w,
1977                                         obj->data.mixerbar.h,
1978                                         mixer_get_right(obj->data.mixerbar.
1979                                                         l) * 255 / 100);
1980                         }
1981
1982                         /* mail stuff */
1983                         OBJ(mails) {
1984                                 snprintf(p, n, "%d", cur->mail_count);
1985                         }
1986                         OBJ(new_mails) {
1987                                 snprintf(p, n, "%d", cur->new_mail_count);
1988                         }
1989 #ifdef MLDONKEY
1990                         OBJ(ml_upload_counter) {
1991                                 snprintf(p, n, "%lld",
1992                                          mlinfo.upload_counter / 1048576);
1993                         }
1994                         OBJ(ml_download_counter) {
1995                                 snprintf(p, n, "%lld",
1996                                          mlinfo.download_counter /
1997                                          1048576);
1998                         }
1999                         OBJ(ml_nshared_files) {
2000                                 snprintf(p, n, "%i", mlinfo.nshared_files);
2001                         }
2002                         OBJ(ml_shared_counter) {
2003                                 snprintf(p, n, "%lld",
2004                                          mlinfo.shared_counter / 1048576);
2005                         }
2006                         OBJ(ml_tcp_upload_rate) {
2007                                 snprintf(p, n, "%.2f",
2008                                          (float) mlinfo.tcp_upload_rate /
2009                                          1024);
2010                         }
2011                         OBJ(ml_tcp_download_rate) {
2012                                 snprintf(p, n, "%.2f",
2013                                          (float) mlinfo.tcp_download_rate /
2014                                          1024);
2015                         }
2016                         OBJ(ml_udp_upload_rate) {
2017                                 snprintf(p, n, "%.2f",
2018                                          (float) mlinfo.udp_upload_rate /
2019                                          1024);
2020                         }
2021                         OBJ(ml_udp_download_rate) {
2022                                 snprintf(p, n, "%.2f",
2023                                          (float) mlinfo.udp_download_rate /
2024                                          1024);
2025                         }
2026                         OBJ(ml_ndownloaded_files) {
2027                                 snprintf(p, n, "%i",
2028                                          mlinfo.ndownloaded_files);
2029                         }
2030                         OBJ(ml_ndownloading_files) {
2031                                 snprintf(p, n, "%i",
2032                                          mlinfo.ndownloading_files);
2033                         }
2034 #endif
2035
2036                         OBJ(nodename) {
2037                                 snprintf(p, n, "%s",
2038                                          cur->uname_s.nodename);
2039                         }
2040                         OBJ(outlinecolor) {
2041                                 new_outline(p, obj->data.l);
2042                         }
2043                         OBJ(processes) {
2044                                 if (!use_spacer)
2045                                         snprintf(p, n, "%d", cur->procs);
2046                                 else
2047                                         snprintf(p, 5, "%d    ",
2048                                                  cur->procs);
2049                         }
2050                         OBJ(running_processes) {
2051                                 if (!use_spacer)
2052                                         snprintf(p, n, "%d",
2053                                                  cur->run_procs);
2054                                 else
2055                                         snprintf(p, 3, "%d     ",
2056                                                  cur->run_procs);
2057                         }
2058                         OBJ(text) {
2059                                 snprintf(p, n, "%s", obj->data.s);
2060                         }
2061                         OBJ(shadecolor) {
2062                                 new_bg(p, obj->data.l);
2063                         }
2064                         OBJ(stippled_hr) {
2065                                 new_stippled_hr(p, obj->data.pair.a,
2066                                                 obj->data.pair.b);
2067                         }
2068                         OBJ(swap) {
2069                                 human_readable(cur->swap * 1024, p, 255);
2070                         }
2071                         OBJ(swapmax) {
2072                                 human_readable(cur->swapmax * 1024, p,
2073                                                255);
2074                         }
2075                         OBJ(swapperc) {
2076                                 if (cur->swapmax == 0) {
2077                                         strncpy(p, "No swap", 255);
2078                                 } else {
2079                                         if (!use_spacer)
2080                                                 snprintf(p, 255, "%*u",
2081                                                          pad_percents,
2082                                                          (cur->swap *
2083                                                           100) /
2084                                                          cur->swapmax);
2085                                         else
2086                                                 snprintf(p, 4, "%*u   ",
2087                                                          pad_percents,
2088                                                          (cur->swap *
2089                                                           100) /
2090                                                          cur->swapmax);
2091                                 }
2092                         }
2093                         OBJ(swapbar) {
2094                                 new_bar(p, obj->data.pair.a,
2095                                         obj->data.pair.b,
2096                                         cur->swapmax ? (cur->swap * 255) /
2097                                         (cur->swapmax) : 0);
2098                         }
2099                         OBJ(sysname) {
2100                                 snprintf(p, n, "%s", cur->uname_s.sysname);
2101                         }
2102                         OBJ(time) {
2103                                 time_t t = time(NULL);
2104                                 struct tm *tm = localtime(&t);
2105                                 setlocale(LC_TIME, "");
2106                                 strftime(p, n, obj->data.s, tm);
2107                         }
2108                         OBJ(utime) {
2109                                 time_t t = time(NULL);
2110                                 struct tm *tm = gmtime(&t);
2111                                 strftime(p, n, obj->data.s, tm);
2112                         }
2113                         OBJ(totaldown) {
2114                                 human_readable(obj->data.net->recv, p,
2115                                                255);
2116                         }
2117                         OBJ(totalup) {
2118                                 human_readable(obj->data.net->trans, p,
2119                                                255);
2120                         }
2121                         OBJ(updates) {
2122                                 snprintf(p, n, "%d", total_updates);
2123                         }
2124                         OBJ(upspeed) {
2125                                 if (!use_spacer)
2126                                         snprintf(p, n, "%d",
2127                                                  (int) (obj->data.net->
2128                                                         trans_speed /
2129                                                         1024));
2130                                 else
2131                                         snprintf(p, 5, "%d     ",
2132                                                  (int) (obj->data.net->
2133                                                         trans_speed /
2134                                                         1024));
2135                         }
2136                         OBJ(upspeedf) {
2137                                 if (!use_spacer)
2138                                         snprintf(p, n, "%.1f",
2139                                                  obj->data.net->
2140                                                  trans_speed / 1024.0);
2141                                 else
2142                                         snprintf(p, 8, "%.1f       ",
2143                                                  obj->data.net->
2144                                                  trans_speed / 1024.0);
2145                         }
2146                         OBJ(upspeedgraph) {
2147                                 if (obj->data.net->trans_speed == 0)    // this is just to make the ugliness at start go away
2148                                         obj->data.net->trans_speed = 0.01;
2149                                 new_graph(p, obj->a, obj->b, obj->c, obj->d,
2150                                           (obj->data.net->trans_speed /
2151                                 1024.0), 1);
2152                         }
2153                         OBJ(uptime_short) {
2154                                 format_seconds_short(p, n,
2155                                                      (int) cur->uptime);
2156                         }
2157                         OBJ(uptime) {
2158                                 format_seconds(p, n, (int) cur->uptime);
2159                         }
2160
2161 #ifdef SETI
2162                         OBJ(seti_prog) {
2163                                 snprintf(p, n, "%.2f",
2164                                          cur->seti_prog * 100.0f);
2165                         }
2166                         OBJ(seti_progbar) {
2167                                 new_bar(p, obj->data.pair.a,
2168                                         obj->data.pair.b,
2169                                         (int) (cur->seti_prog * 255.0f));
2170                         }
2171                         OBJ(seti_credit) {
2172                                 snprintf(p, n, "%.0f", cur->seti_credit);
2173                         }
2174 #endif
2175
2176 #ifdef MPD
2177                         OBJ(mpd_title) {
2178                                 snprintf(p, n, "%s", cur->mpd.title);
2179                         }
2180                         OBJ(mpd_artist) {
2181                                 snprintf(p, n, "%s", cur->mpd.artist);
2182                         }
2183                         OBJ(mpd_album) {
2184                                 snprintf(p, n, "%s", cur->mpd.album);
2185                         }
2186                         OBJ(mpd_vol) {
2187                                 snprintf(p, n, "%i", cur->mpd.volume);
2188                         }
2189                         OBJ(mpd_bitrate) {
2190                                 snprintf(p, n, "%i", cur->mpd.bitrate);
2191                         }
2192                         OBJ(mpd_status) {
2193                                 snprintf(p, n, "%s", cur->mpd.status);
2194                         }
2195                         OBJ(mpd_elapsed) {
2196                                 int days = 0, hours = 0, minutes =
2197                                     0, seconds = 0;
2198                                 int tmp = cur->mpd.elapsed;
2199                                 while (tmp >= 86400) {
2200                                         tmp -= 86400;
2201                                         days++;
2202                                 }
2203                                 while (tmp >= 3600) {
2204                                         tmp -= 3600;
2205                                         hours++;
2206                                 }
2207                                 while (tmp >= 60) {
2208                                         tmp -= 60;
2209                                         minutes++;
2210                                 }
2211                                 seconds = tmp;
2212                                 if (days > 0)
2213                                         snprintf(p, n, "%i days %i:%i:%2i",
2214                                                  days, hours, minutes,
2215                                                  seconds);
2216                                 else if (days > 0)
2217                                         snprintf(p, n, "%i:%i:%02i", hours,
2218                                                  minutes, seconds);
2219                                 else
2220                                         snprintf(p, n, "%i:%02i", minutes,
2221                                                  seconds);
2222                         }
2223                         OBJ(mpd_length) {
2224                                 int days = 0, hours = 0, minutes =
2225                                     0, seconds = 0;
2226                                 int tmp = cur->mpd.length;
2227                                 while (tmp >= 86400) {
2228                                         tmp -= 86400;
2229                                         days++;
2230                                 }
2231                                 while (tmp >= 3600) {
2232                                         tmp -= 3600;
2233                                         hours++;
2234                                 }
2235                                 while (tmp >= 60) {
2236                                         tmp -= 60;
2237                                         minutes++;
2238                                 }
2239                                 seconds = tmp;
2240                                 if (days > 0)
2241                                         snprintf(p, n,
2242                                                  "%i days %i:%i:%02i",
2243                                                  days, hours, minutes,
2244                                                  seconds);
2245                                 else if (days > 0)
2246                                         snprintf(p, n, "%i:%i:%02i", hours,
2247                                                  minutes, seconds);
2248                                 else
2249                                         snprintf(p, n, "%i:%02i", minutes,
2250                                                  seconds);
2251                         }
2252                         OBJ(mpd_percent) {
2253                                 snprintf(p, n, "%2.0f",
2254                                          cur->mpd.progress * 100);
2255                         }
2256                         OBJ(mpd_bar) {
2257                                 new_bar(p, obj->data.pair.a,
2258                                         obj->data.pair.b,
2259                                         (int) (cur->mpd.progress *
2260                                                255.0f));
2261                         }
2262 #endif
2263                         OBJ(top) {
2264                                 if (obj->data.top.type == TOP_NAME
2265                                     && obj->data.top.num >= 0
2266                                     && obj->data.top.num < 10) {
2267                                         // if we limit the buffer and add a bunch of space after, it stops the thing from
2268                                         // moving other shit around, which is really fucking annoying
2269                                         snprintf(p, 17,
2270                                                  "%s                              ",
2271                                                  cur->cpu[obj->data.top.
2272                                                           num]->name);
2273                                 } else if (obj->data.top.type == TOP_CPU
2274                                            && obj->data.top.num >= 0
2275                                            && obj->data.top.num < 10) {
2276                                         snprintf(p, 7, "%3.2f      ",
2277                                                  cur->cpu[obj->data.top.
2278                                                           num]->amount);
2279                                 } else if (obj->data.top.type == TOP_PID
2280                                            && obj->data.top.num >= 0
2281                                            && obj->data.top.num < 10) {
2282                                         snprintf(p, 8, "%i           ",
2283                                                  cur->cpu[obj->data.top.
2284                                                           num]->pid);
2285                                 } else if (obj->data.top.type == TOP_MEM
2286                                            && obj->data.top.num >= 0
2287                                            && obj->data.top.num < 10) {
2288                                         snprintf(p, 7, "%3.2f       ",
2289                                                  cur->cpu[obj->data.top.
2290                                                           num]->totalmem);
2291                                 }
2292                         }
2293                         OBJ(top_mem) {
2294                                 if (obj->data.top.type == TOP_NAME
2295                                     && obj->data.top.num >= 0
2296                                     && obj->data.top.num < 10) {
2297                                         // if we limit the buffer and add a bunch of space after, it stops the thing from
2298                                         // moving other shit around, which is really fucking annoying
2299                                         snprintf(p, 17,
2300                                                  "%s                              ",
2301                                                  cur->memu[obj->data.top.
2302                                                            num]->name);
2303                                 } else if (obj->data.top.type == TOP_CPU
2304                                            && obj->data.top.num >= 0
2305                                            && obj->data.top.num < 10) {
2306                                         snprintf(p, 7, "%3.2f      ",
2307                                                  cur->memu[obj->data.top.
2308                                                            num]->amount);
2309                                 } else if (obj->data.top.type == TOP_PID
2310                                            && obj->data.top.num >= 0
2311                                            && obj->data.top.num < 10) {
2312                                         snprintf(p, 8, "%i           ",
2313                                                  cur->memu[obj->data.top.
2314                                                            num]->pid);
2315                                 } else if (obj->data.top.type == TOP_MEM
2316                                            && obj->data.top.num >= 0
2317                                            && obj->data.top.num < 10) {
2318                                         snprintf(p, 7, "%3.2f       ",
2319                                                  cur->memu[obj->data.top.
2320                                                            num]->totalmem);
2321                                 }
2322                         }
2323
2324
2325
2326                         /*
2327                          * I'm tired of everything being packed in
2328                          * pee
2329                          * poop
2330                          */
2331
2332
2333                         OBJ(tail) {
2334                                 if (current_update_time -obj->data.tail.last_update < obj->data.tail.interval) {
2335                                         snprintf(p, n, "%s", obj->data.tail.buffer);
2336                                 } else {
2337                                         obj->data.tail.last_update = current_update_time;
2338                                         FILE *fp;
2339                                         int i;
2340                                         tailstring *head = NULL;
2341                                         tailstring *headtmp = NULL;
2342                                         fp = fopen(obj->data.tail.logfile, "rt");
2343                                         if (fp == NULL)
2344                                                 ERR("tail logfile failed to open");
2345                                         else {
2346                                                 obj->data.tail.readlines = 0;
2347
2348                                                 while (fgets(obj->data.tail.buffer, TEXT_BUFFER_SIZE*4, fp) != NULL) {
2349                                                         addtail(&head, obj->data.tail.buffer);
2350                                                         obj->data.tail.readlines++;
2351                                                 }
2352
2353                                                 fclose(fp);
2354
2355                                                 if (obj->data.tail.readlines > 0) {
2356                                                         for (i = 0;i < obj->data.tail.wantedlines + 1 && i < obj->data.tail.readlines; i++) {
2357                                                                 addtail(&headtmp, head->data);
2358                                                                 head = head->next;
2359                                                         }
2360                                                         strcpy(obj->data.tail.buffer, headtmp->data);
2361                                                         headtmp = headtmp->next;
2362                                                         for (i = 1;i < obj->data.tail.wantedlines + 1 && i < obj->data.tail.readlines; i++) {
2363                                                                 if (headtmp) {
2364                                                                         strncat(obj->data.tail.buffer, headtmp->data, TEXT_BUFFER_SIZE * 4 / obj->data.tail.wantedlines);
2365                                                                         headtmp = headtmp->next;
2366                                                                 }
2367                                                         }
2368
2369                                                         /* get rid of any ugly newlines at the end */
2370                                                         if (obj->data.tail.buffer[strlen(obj->data.tail.buffer)-1] == '\n') {
2371                                                                 obj->data.tail.buffer[strlen(obj->data.tail.buffer)-1] = '\0';
2372                                                         }
2373                                                         snprintf(p, n, "%s", obj->data.tail.buffer);
2374
2375                                                         freetail(headtmp);
2376                                                 }
2377                                                 else {
2378                                                         strcpy(obj->data.tail.buffer, "Logfile Empty");
2379                                                         snprintf(p, n, "Logfile Empty");
2380                                                 }
2381                                                 freetail(head);
2382                                         }
2383                                 }
2384                         }
2385
2386
2387 #ifdef METAR
2388                         // Hmm, it's expensive to calculate this shit every time FIXME
2389                         OBJ(metar_ob_time) {
2390                                 if (data.ob_hour != INT_MAX
2391                                     && data.ob_minute != INT_MAX
2392                                     && metar_worked)
2393                                         format_seconds(p, n,
2394                                                        data.ob_hour *
2395                                                        3600 +
2396                                                        data.ob_minute *
2397                                                        60);
2398                                 else
2399                                         format_seconds(p, n, 0);
2400                         }
2401                         OBJ(metar_temp) {
2402                                 if (data.temp != INT_MAX && metar_worked)
2403                                         snprintf(p, n, "%i", data.temp);
2404                                 else
2405                                         snprintf(p, n, "-");
2406                         }
2407                         OBJ(metar_tempf) {
2408                                 if (data.temp != INT_MAX && metar_worked)
2409                                         snprintf(p, n, "%3.1f",
2410                                                  (data.temp +
2411                                                   40) * 9.0 / 5 - 40);
2412                                 else
2413                                         snprintf(p, n, "-");
2414                         }
2415                         OBJ(metar_windchill) {
2416                                 if (data.temp != INT_MAX
2417                                     && data.winData.windSpeed != INT_MAX
2418                                     && metar_worked)
2419                                         snprintf(p, n, "%i",
2420                                                  calculateWindChill(data.
2421                                                                     temp,
2422                                                                     data.
2423                                                                     winData.
2424                                                                     windSpeed));
2425                                 else
2426                                         snprintf(p, n, "-");
2427                         }
2428                         OBJ(metar_dew_point) {
2429                                 if (data.dew_pt_temp != INT_MAX
2430                                     && metar_worked)
2431                                         snprintf(p, n, "%i",
2432                                                  data.dew_pt_temp);
2433                                 else
2434                                         snprintf(p, n, "-");
2435                         }
2436                         OBJ(metar_rh) {
2437                                 if (data.temp != INT_MAX
2438                                     && data.dew_pt_temp != INT_MAX
2439                                     && metar_worked)
2440                                         snprintf(p, n, "%i",
2441                                                  calculateRelativeHumidity
2442                                                  (data.temp,
2443                                                   data.dew_pt_temp));
2444                                 else
2445                                         snprintf(p, n, "-");
2446                         }
2447
2448                         OBJ(metar_windspeed) {
2449                                 if (data.winData.windSpeed != INT_MAX
2450                                     && metar_worked)
2451                                         snprintf(p, n, "%i",
2452                                                  knTokph(data.winData.
2453                                                          windSpeed));
2454                                 else
2455                                         snprintf(p, n, "-");
2456                         }
2457                         OBJ(metar_winddir) {
2458                                 if (data.winData.windDir != INT_MAX
2459                                     && metar_worked)
2460                                         snprintf(p, n, "%s",
2461                                                  calculateWindDirectionString
2462                                                  (data.winData.windDir));
2463                                 else
2464                                         snprintf(p, n, "-");
2465                         }
2466                         OBJ(metar_swinddir) {
2467                                 if (data.winData.windDir != INT_MAX
2468                                     && metar_worked)
2469                                         snprintf(p, n, "%s",
2470                                                  calculateShortWindDirectionString
2471                                                  (data.winData.windDir));
2472                                 else
2473                                         snprintf(p, n, "-");
2474                         }
2475
2476                         OBJ(metar_cloud) {
2477                                 if (data.cldTypHgt[0].cloud_type[0] != '\0'
2478                                     && metar_worked) {
2479                                         if (strcmp
2480                                             (&data.cldTypHgt[0].
2481                                              cloud_type[0], "SKC") == 0)
2482                                                 snprintf(p, n,
2483                                                          "Clear Sky");
2484                                         else if (strcmp
2485                                                  (&data.cldTypHgt[0].
2486                                                   cloud_type[0],
2487                                                   "CLR") == 0)
2488                                                 snprintf(p, n,
2489                                                          "Clear Sky");
2490                                         else if (strcmp
2491                                                  (&data.cldTypHgt[0].
2492                                                   cloud_type[0],
2493                                                   "FEW") == 0)
2494                                                 snprintf(p, n,
2495                                                          "Few clouds");
2496                                         else if (strcmp
2497                                                  (&data.cldTypHgt[0].
2498                                                   cloud_type[0],
2499                                                   "SCT") == 0)
2500                                                 snprintf(p, n,
2501                                                          "Scattered clouds");
2502                                         else if (strcmp
2503                                                  (&data.cldTypHgt[0].
2504                                                   cloud_type[0],
2505                                                   "BKN") == 0)
2506                                                 snprintf(p, n,
2507                                                          "Broken clouds");
2508                                         else if (strcmp
2509                                                  (&data.cldTypHgt[0].
2510                                                   cloud_type[0],
2511                                                   "OVC") == 0)
2512                                                 snprintf(p, n, "Overcast");
2513                                         else
2514                                                 snprintf(p, n,
2515                                                          "Checking...");
2516                                 } else
2517                                         snprintf(p, n, "Checking...");
2518                         }
2519                         OBJ(metar_u2d_time) {
2520                                 format_seconds(p, n,
2521                                                (int) last_metar_update %
2522                                                (3600 * 24) + 3600);
2523                         }
2524 #endif
2525
2526                         break;
2527                 }
2528
2529                 {
2530                         unsigned int a = strlen(p);
2531                         p += a;
2532                         n -= a;
2533                 }
2534         }
2535
2536         if (stuff_in_upper_case) {
2537                 char *p;
2538
2539                 p = text_buffer;
2540                 while (*p) {
2541                         *p = toupper(*p);
2542                         p++;
2543                 }
2544         }
2545
2546         last_update_time = current_update_time;
2547         total_updates++;
2548         //free(p);
2549 }
2550
2551
2552 static void set_font()
2553 {
2554 #ifdef XFT
2555         if (use_xft) {
2556                         if (window.xftdraw != NULL)
2557                                 XftDrawDestroy(window.xftdraw);
2558                         window.xftdraw = XftDrawCreate(display, window.drawable,
2559                                         DefaultVisual(display,
2560                                                         screen),
2561                                         DefaultColormap(display,
2562                                                         screen));
2563
2564                 } else
2565 #endif
2566 {
2567         XSetFont(display, window.gc, fonts[selected_font].font->fid);
2568 }
2569 }
2570
2571
2572 /*
2573  * text size
2574  */
2575
2576 static int text_start_x, text_start_y;  /* text start position in window */
2577 static int text_width, text_height;
2578
2579 static inline int get_string_width(const char *s)
2580 {
2581         return *s ? calc_text_width(s, strlen(s)) : 0;
2582 }
2583
2584 int fontchange = 0;
2585
2586 static void text_size_updater(char *s)
2587 {
2588         int w = 0;
2589         char *p;
2590         int h = font_height();
2591         /* get string widths and skip specials */
2592         p = s;
2593         while (*p) {
2594                 if (*p == SPECIAL_CHAR) {
2595                         *p = '\0';
2596                         w += get_string_width(s);
2597                         *p = SPECIAL_CHAR;
2598
2599                         if (specials[special_index].type == BAR
2600                             || specials[special_index].type == GRAPH) {
2601                                 w += specials[special_index].width;
2602                                 if (specials[special_index].height > h) {
2603                                         h = specials[special_index].height;
2604                                         h += font_ascent();
2605                                 }
2606                         }
2607                         
2608                         else if (specials[special_index].type == OFFSET) {
2609                                 w += specials[special_index].arg + get_string_width("a"); /* filthy, but works */
2610                         }
2611                         
2612                         else if (specials[special_index].type == FONT) {
2613                                 fontchange = specials[special_index].font_added;
2614                                 selected_font = specials[special_index].font_added;
2615                                 h = font_height();
2616                         }
2617
2618                         
2619                         special_index++;
2620                         s = p + 1;
2621                 }
2622                 p++;
2623         }
2624                 w += get_string_width(s);
2625         if (w > text_width)
2626                 text_width = w;
2627
2628         text_height += h;
2629         if (fontchange) {
2630                 selected_font = 0;
2631         }
2632 }
2633
2634 static void update_text_area()
2635 {
2636         int x, y;
2637
2638         /* update text size if it isn't fixed */
2639 #ifdef OWN_WINDOW
2640         if (!fixed_size)
2641 #endif
2642         {
2643                 text_width = minimum_width;
2644                 text_height = 0;
2645                 special_index = 0;
2646                 for_each_line(text_buffer, text_size_updater);
2647                 text_width += 1;
2648                 if (text_height < minimum_height)
2649                         text_height = minimum_height;
2650         }
2651
2652         /* get text position on workarea */
2653         switch (text_alignment) {
2654         case TOP_LEFT:
2655                 x = gap_x;
2656                 y = gap_y;
2657                 break;
2658
2659         case TOP_RIGHT:
2660                 x = workarea[2] - text_width - gap_x;
2661                 y = gap_y;
2662                 break;
2663
2664         default:
2665         case BOTTOM_LEFT:
2666                 x = gap_x;
2667                 y = workarea[3] - text_height - gap_y;
2668                 break;
2669
2670         case BOTTOM_RIGHT:
2671                 x = workarea[2] - text_width - gap_x;
2672                 y = workarea[3] - text_height - gap_y;
2673                 break;
2674         }
2675
2676 #ifdef OWN_WINDOW
2677         if (own_window) {
2678                 x += workarea[0];
2679                 y += workarea[1];
2680                 text_start_x = border_margin + 1;
2681                 text_start_y = border_margin + 1;
2682                 window.x = x - border_margin - 1;
2683                 window.y = y - border_margin - 1;
2684         } else
2685 #endif
2686         {
2687                 /* If window size doesn't match to workarea's size, then window
2688                  * probably includes panels (gnome).
2689                  * Blah, doesn't work on KDE. */
2690                 if (workarea[2] != window.width
2691                     || workarea[3] != window.height) {
2692                         y += workarea[1];
2693                         x += workarea[0];
2694                 }
2695
2696                 text_start_x = x;
2697                 text_start_y = y;
2698         }
2699 }
2700
2701 /*
2702  * drawing stuff
2703  */
2704
2705 static int cur_x, cur_y;        /* current x and y for drawing */
2706 static int draw_mode;           /* FG, BG or OUTLINE */
2707 static long current_color;
2708
2709 static inline void set_foreground_color(long c)
2710 {
2711         current_color = c;
2712         XSetForeground(display, window.gc, c);
2713 }
2714
2715 static void draw_string(const char *s)
2716 {
2717         if (s[0] == '\0')
2718                 return;
2719         int i, i2, pos, width_of_s;
2720         int max, added;
2721         width_of_s = get_string_width(s);
2722         if (out_to_console) {
2723                 printf("%s\n", s);
2724         }
2725         strcpy(tmpstring1, s);
2726         pos = 0;
2727         added = 0;
2728         char space[2];
2729         snprintf(space, 2, " ");
2730         max = ((text_width - width_of_s) / get_string_width(space));
2731         /*
2732          * This code looks for tabs in the text and coverts them to spaces.
2733          * The trick is getting the correct number of spaces,
2734          * and not going over the window's size without forcing
2735          * the window larger.
2736          */
2737         for (i = 0; i < TEXT_BUFFER_SIZE; i++) {
2738                 if (tmpstring1[i] == '\t')      // 9 is ascii tab
2739                 {
2740                         i2 = 0;
2741                         for (i2 = 0;
2742                              i2 < (8 - (1 + pos) % 8) && added <= max;
2743                              i2++) {
2744                                 tmpstring2[pos + i2] = ' ';
2745                                 added++;
2746                         }
2747                         pos += i2;
2748                 } else {
2749                         if (tmpstring1[i] != 9) {
2750                                 tmpstring2[pos] = tmpstring1[i];
2751                                 pos++;
2752                         }
2753                 }
2754         }
2755         s = tmpstring2;
2756 #ifdef XFT
2757         if (use_xft) {
2758                 XColor c;
2759                 XftColor c2;
2760                 c.pixel = current_color;
2761                 XQueryColor(display, DefaultColormap(display, screen), &c);
2762
2763                 c2.pixel = c.pixel;
2764                 c2.color.red = c.red;
2765                 c2.color.green = c.green;
2766                 c2.color.blue = c.blue;
2767                 c2.color.alpha = fonts[selected_font].font_alpha;
2768                 if (utf8_mode) {
2769                         XftDrawStringUtf8(window.xftdraw, &c2, fonts[selected_font].xftfont,
2770                                           cur_x, cur_y, (XftChar8 *) s,
2771                                           strlen(s));
2772                 } else {
2773                         XftDrawString8(window.xftdraw, &c2, fonts[selected_font].xftfont,
2774                                        cur_x, cur_y, (XftChar8 *) s,
2775                                        strlen(s));
2776                 }
2777         } else
2778 #endif
2779         {
2780                 XDrawString(display, window.drawable, window.gc,
2781                             cur_x, cur_y, s, strlen(s));
2782         }
2783         memcpy(tmpstring1, s, TEXT_BUFFER_SIZE);
2784         cur_x += width_of_s;
2785 }
2786
2787 inline unsigned long do_gradient(unsigned long first_colour, unsigned long last_colour) { /* this function returns the next colour between two colours for a gradient */
2788         int tmp_color = 0;
2789         int red1, green1, blue1; // first colour
2790         int red2, green2, blue2; // second colour
2791         int red3 = 0, green3 = 0, blue3 = 0; // difference
2792         red1 = (first_colour & 0xff0000) >> 16;
2793         green1 = (first_colour & 0xff00) >> 8;
2794         blue1 = first_colour & 0xff;
2795         red2 = (last_colour & 0xff0000) >> 16;
2796         green2 = (last_colour & 0xff00) >> 8;
2797         blue2 = last_colour & 0xff;
2798         if (red1 > red2) {
2799                 red3 = -1;
2800         }
2801         if (red1 < red2) {
2802                 red3 = 1;
2803         }
2804         if (green1 > green2) {
2805                 green3 = -1;
2806         }
2807         if (green1 < green2) {
2808                 green3 = 1;
2809         }
2810         if (blue1 > blue2) {
2811                 blue3 = -1;
2812         }
2813         if (blue1 < blue2) {
2814                 blue3 = 1;
2815         }
2816         red1 += red3;
2817         green1 += green3;
2818         blue1 += blue3;
2819         if (red1 < 0) {
2820                 red1 = 0;
2821         }
2822         if (green1 < 0) {
2823                 green1 = 0;
2824         }
2825         if (blue1 < 0) {
2826                 blue1 = 0;
2827         }
2828         if (red1 > 0xff) {
2829                 red1 = 0xff;
2830         }
2831         if (green1 > 0xff) {
2832                 green1 = 0xff;
2833         }
2834         if (blue1 > 0xff) {
2835                 blue1 = 0xff;
2836         }
2837         tmp_color = (red1 << 16) | (green1 << 8) | blue1;
2838         return tmp_color;
2839 }
2840
2841 inline unsigned long gradient_max(unsigned long first_colour, unsigned long last_colour) { /* this function returns the max diff for a gradient */
2842         int red1, green1, blue1; // first colour
2843         int red2, green2, blue2; // second colour
2844         int red3 = 0, green3 = 0, blue3 = 0; // difference
2845         red1 = (first_colour & 0xff0000) >> 16;
2846         green1 = (first_colour & 0xff00) >> 8;
2847         blue1 = first_colour & 0xff;
2848         red2 = (last_colour & 0xff0000) >> 16;
2849         green2 = (last_colour & 0xff00) >> 8;
2850         blue2 = last_colour & 0xff;
2851         red3 = abs(red1 - red2);
2852         green3 = abs(green1 - green2);
2853         blue3 = abs(blue1 - blue2);
2854         int max = red3;
2855         if (green3 > max)
2856                 max = green3;
2857         if (blue3 > max)
2858                 max = blue3;
2859         return max;
2860 }
2861
2862
2863 static void draw_line(char *s)
2864 {
2865         char *p;
2866
2867         cur_x = text_start_x;
2868         cur_y += font_ascent();
2869         int cur_y_add = 0;
2870         short font_h = font_height();
2871
2872         /* find specials and draw stuff */
2873         p = s;
2874         while (*p) {
2875                 if (*p == SPECIAL_CHAR) {
2876                         int w = 0;
2877
2878                         /* draw string before special */
2879                         *p = '\0';
2880                         draw_string(s);
2881                         *p = SPECIAL_CHAR;
2882                         s = p + 1;
2883
2884                         /* draw special */
2885                         switch (specials[special_index].type) {
2886                         case HORIZONTAL_LINE:
2887                                 {
2888                                         int h =
2889                                             specials[special_index].height;
2890                                         int mid = font_ascent() / 2;
2891                                         w = text_start_x + text_width -
2892                                             cur_x;
2893
2894                                         XSetLineAttributes(display,
2895                                                            window.gc, h,
2896                                                            LineSolid,
2897                                                            CapButt,
2898                                                            JoinMiter);
2899                                         XDrawLine(display, window.drawable,
2900                                                   window.gc, cur_x,
2901                                                   cur_y - mid / 2,
2902                                                   cur_x + w,
2903                                                   cur_y - mid / 2);
2904                                 }
2905                                 break;
2906
2907                         case STIPPLED_HR:
2908                                 {
2909                                         int h =
2910                                             specials[special_index].height;
2911                                         int s =
2912                                             specials[special_index].arg;
2913                                         int mid = font_ascent() / 2;
2914                                         char ss[2] = { s, s };
2915                                         w = text_start_x + text_width -
2916                                             cur_x - 1;
2917
2918                                         XSetLineAttributes(display,
2919                                                            window.gc, h,
2920                                                            LineOnOffDash,
2921                                                            CapButt,
2922                                                            JoinMiter);
2923                                         XSetDashes(display, window.gc, 0,
2924                                                    ss, 2);
2925                                         XDrawLine(display, window.drawable,
2926                                                   window.gc, cur_x,
2927                                                   cur_y - mid / 2,
2928                                                   cur_x + w,
2929                                                   cur_y - mid / 2);
2930                                 }
2931                                 break;
2932
2933                         case BAR:
2934                                 {
2935                                         int h =
2936                                             specials[special_index].height;
2937                                         int bar_usage =
2938                                             specials[special_index].arg;
2939                                         int by =
2940                                             cur_y - (font_ascent() +
2941                                                      h) / 2 - 1;
2942                                         w = specials[special_index].width;
2943                                         if (w == 0)
2944                                                 w = text_start_x +
2945                                                     text_width - cur_x - 1;
2946                                         if (w < 0)
2947                                                 w = 0;
2948
2949                                         XSetLineAttributes(display,
2950                                                            window.gc, 1,
2951                                                            LineSolid,
2952                                                            CapButt,
2953                                                            JoinMiter);
2954
2955                                         XDrawRectangle(display,
2956                                                        window.drawable,
2957                                                        window.gc, cur_x,
2958                                                        by, w, h);
2959                                         XFillRectangle(display,
2960                                                        window.drawable,
2961                                                        window.gc, cur_x,
2962                                                        by,
2963                                                        w * bar_usage / 255,
2964                                                        h);
2965                                         if (specials[special_index].
2966                                             height > cur_y_add
2967                                             && specials[special_index].
2968                                             height > font_h) {
2969                                                 cur_y_add =
2970                                                     specials
2971                                                     [special_index].height;
2972                                         }
2973                                 }
2974                                 break;
2975
2976                         case GRAPH:
2977                                 {
2978                                         int h =
2979                                             specials[special_index].height;
2980                                         int by;
2981 #ifdef XFT
2982                                         if (use_xft) {
2983                                             by = cur_y - (font_ascent() +
2984                                                      h) / 2 - 1;
2985                                         } else
2986 #endif
2987                                         {
2988                                                 by = cur_y - (font_ascent()/2);
2989                                         }
2990                                         w = specials[special_index].width;
2991                                         if (w == 0)
2992                                                 w = text_start_x + text_width - cur_x - 1;
2993                                         if (w < 0)
2994                                                 w = 0;
2995                                         XSetLineAttributes(display,
2996                                                            window.gc, 1,
2997                                                            LineSolid,
2998                                                            CapButt,
2999                                                            JoinMiter);
3000                                         XDrawRectangle(display,
3001                                                        window.drawable,
3002                                                        window.gc, cur_x,
3003                                                        by, w, h);
3004                                         XSetLineAttributes(display,
3005                                                            window.gc, 1,
3006                                                            LineSolid,
3007                                                            CapButt,
3008                                                            JoinMiter);
3009         int i;
3010         int j = 0;
3011         int gradient_size = 0;
3012         float gradient_factor = 0;
3013         float gradient_update = 0;
3014         unsigned int tmpcolour = current_color;
3015         if (specials[special_index].first_colour != specials[special_index].last_colour) {
3016                 tmpcolour = specials[special_index].first_colour;
3017                 gradient_size = gradient_max(specials[special_index].first_colour, specials[special_index].last_colour);
3018                 gradient_factor = (float)gradient_size / (w - 3);
3019         }
3020         for (i = 0; i < w - 3; i++) {
3021                 if (specials[special_index].first_colour != specials[special_index].last_colour) {
3022                         XSetForeground(display, window.gc, tmpcolour);
3023                         gradient_update += gradient_factor;
3024                         while (gradient_update > 0) {
3025                                 tmpcolour = do_gradient(tmpcolour, specials[special_index].last_colour);
3026                                 gradient_update--;
3027                         }
3028                 }
3029                 if (i /
3030                                                 ((float) (w - 3) /
3031                                                 (specials
3032                                                 [special_index].
3033                                                 graph_width)) > j) {
3034                         j++;
3035                                                 }
3036                                                 XDrawLine(display,  window.drawable, window.gc, cur_x + i + 2, by + h, cur_x + i + 2, by + h - specials[special_index].graph[j] * (h - 1) / specials[special_index].graph_scale);       /* this is mugfugly, but it works */
3037         }
3038                                         if (specials[special_index].
3039                                             height > cur_y_add
3040                                             && specials[special_index].
3041                                             height > font_h) {
3042                                                 cur_y_add =
3043                                                     specials
3044                                                     [special_index].height;
3045                                         }
3046                                 }
3047                                 if (draw_mode == BG) {
3048                                         set_foreground_color(default_bg_color);
3049                                 }
3050                                 else if (draw_mode == OUTLINE) {
3051                                         set_foreground_color(default_out_color);
3052                                 } else {
3053                                         set_foreground_color(default_fg_color);
3054                                 }
3055                                 break;
3056                         
3057                                 case FONT:
3058                                 if (fontchange) {
3059                                         cur_y -= font_ascent();
3060                                         selected_font = specials[special_index].font_added;
3061                                         cur_y += font_ascent();
3062 #ifdef XFT
3063                                         if (!use_xft)
3064 #endif
3065                                         {
3066                                                 set_font();
3067                                         }
3068                                 }
3069                                                 
3070                                 break;
3071                         case FG:
3072                                 if (draw_mode == FG)
3073                                         set_foreground_color(specials
3074                                                              [special_index].
3075                                                              arg);
3076                                 break;
3077
3078                         case BG:
3079                                 if (draw_mode == BG)
3080                                         set_foreground_color(specials
3081                                                              [special_index].
3082                                                              arg);
3083                                 break;
3084
3085                         case OUTLINE:
3086                                 if (draw_mode == OUTLINE)
3087                                         set_foreground_color(specials
3088                                                              [special_index].
3089                                                              arg);
3090                                 break;
3091
3092                         case OFFSET:
3093                                 {
3094                                         w = text_start_x + specials[special_index].arg;
3095                                 }
3096                         break;
3097
3098                         case ALIGNR:
3099                                 {
3100                                         int pos_x =
3101                                             text_start_x + text_width -
3102                                             cur_x - 1 -
3103                                             get_string_width(p);
3104                                         if (pos_x >
3105                                             specials[special_index].arg)
3106                                                 w = pos_x -
3107                                                     specials
3108                                                     [special_index].arg;
3109                                 }
3110                                 break;
3111
3112                         case ALIGNC:
3113                                 {
3114                                         int pos_x =
3115                                             text_start_x + text_width -
3116                                             cur_x - 1 -
3117                                             get_string_width(p) / 2 -
3118                                             (text_width / 2);
3119                                         if (pos_x >
3120                                             specials[special_index].arg)
3121                                                 w = pos_x -
3122                                                     specials
3123                                                     [special_index].arg;
3124                                 }
3125                                 break;
3126
3127                         }
3128
3129                         cur_x += w;
3130
3131                         special_index++;
3132                 }
3133
3134                 p++;
3135         }
3136         if (cur_y_add > 0) {
3137                 cur_y += cur_y_add;
3138                 cur_y -= font_descent();
3139         }
3140
3141         draw_string(s);
3142
3143         cur_y += font_descent();
3144         if (fontchange) {
3145                 selected_font = 0;
3146         }
3147 }
3148
3149 static void draw_text()
3150 {
3151         cur_y = text_start_y;
3152
3153         /* draw borders */
3154         if (draw_borders && border_width > 0) {
3155                 unsigned int b = (border_width + 1) / 2;
3156
3157                 if (stippled_borders) {
3158                         char ss[2] =
3159                             { stippled_borders, stippled_borders };
3160                         XSetLineAttributes(display, window.gc,
3161                                            border_width, LineOnOffDash,
3162                                            CapButt, JoinMiter);
3163                         XSetDashes(display, window.gc, 0, ss, 2);
3164                 } else {
3165                         XSetLineAttributes(display, window.gc,
3166                                            border_width, LineSolid,
3167                                            CapButt, JoinMiter);
3168                 }
3169
3170                 XDrawRectangle(display, window.drawable, window.gc,
3171                                text_start_x - border_margin + b,
3172                                text_start_y - border_margin + b,
3173                                text_width + border_margin * 2 - 1 - b * 2,
3174                                text_height + border_margin * 2 - 1 -
3175                                b * 2);
3176         }
3177
3178         /* draw text */
3179         special_index = 0;
3180         for_each_line(text_buffer, draw_line);
3181 }
3182
3183 static void draw_stuff()
3184 {
3185         if (draw_shades && !draw_outline) {
3186                 text_start_x++;
3187                 text_start_y++;
3188                 set_foreground_color(default_bg_color);
3189                 draw_mode = BG;
3190                 draw_text();
3191                 text_start_x--;
3192                 text_start_y--;
3193         }
3194
3195         if (draw_outline) {
3196                 int i, j;
3197                 for (i = -1; i < 2; i++)
3198                         for (j = -1; j < 2; j++) {
3199                                 if (i == 0 && j == 0)
3200                                         continue;
3201                                 text_start_x += i;
3202                                 text_start_y += j;
3203                                 set_foreground_color(default_out_color);
3204                                 draw_mode = OUTLINE;
3205                                 draw_text();
3206                                 text_start_x -= i;
3207                                 text_start_y -= j;
3208                         }
3209         }
3210
3211         set_foreground_color(default_fg_color);
3212         draw_mode = FG;
3213         draw_text();
3214
3215 #ifdef XDBE
3216         if (use_xdbe) {
3217                 XdbeSwapInfo swap;
3218                 swap.swap_window = window.window;
3219                 swap.swap_action = XdbeBackground;
3220                 XdbeSwapBuffers(display, &swap, 1);
3221         }
3222 #endif
3223 /*#ifdef METAR wtf is this for exactly? aside from trying to cause segfaults?
3224 if (metar_station != NULL) {
3225         free(metar_station);
3226         metar_station = NULL;
3227 }
3228 if (metar_server != NULL) {
3229         free(metar_server);
3230         metar_server = NULL;
3231 }
3232 if (metar_path != NULL) {
3233         free(metar_path);
3234         metar_path = NULL;
3235 }
3236 #endif*/
3237
3238 }
3239
3240 static void clear_text(int exposures)
3241 {
3242 #ifdef XDBE
3243         if (use_xdbe)
3244                 return;         /* The swap action is XdbeBackground, which clears */
3245 #endif
3246         /* there is some extra space for borders and outlines */
3247         XClearArea(display, window.drawable,
3248                    text_start_x - border_margin - 1,
3249                    text_start_y - border_margin - 1,
3250                    text_width + border_margin * 2 + 2,
3251                    text_height + border_margin * 2 + 2,
3252                    exposures ? True : 0);
3253 }
3254
3255 static int need_to_update;
3256
3257 /* update_text() generates new text and clears old text area */
3258 static void update_text()
3259 {
3260         generate_text();
3261         clear_text(1);
3262         need_to_update = 1;
3263 }
3264
3265 static void main_loop()
3266 {
3267         Region region = XCreateRegion();
3268         info.looped = 0;
3269         while (total_run_times == 0 || info.looped < total_run_times - 1) {
3270                 info.looped++;
3271                 XFlush(display);
3272
3273                 /* wait for X event or timeout */
3274
3275                 if (!XPending(display)) {
3276                         fd_set fdsr;
3277                         struct timeval tv;
3278                         int s;
3279                         double t =
3280                             update_interval - (get_time() -
3281                                                last_update_time);
3282
3283                         if (t < 0)
3284                                 t = 0;
3285
3286                         tv.tv_sec = (long) t;
3287                         tv.tv_usec = (long) (t * 1000000) % 1000000;
3288
3289                         FD_ZERO(&fdsr);
3290                         FD_SET(ConnectionNumber(display), &fdsr);
3291
3292                         s = select(ConnectionNumber(display) + 1, &fdsr, 0,
3293                                    0, &tv);
3294                         if (s == -1) {
3295                                 if (errno != EINTR)
3296                                         ERR("can't select(): %s",
3297                                             strerror(errno));
3298                         } else {
3299                                 /* timeout */
3300                                 if (s == 0)
3301                                         update_text();
3302                         }
3303                 }
3304
3305                 if (need_to_update) {
3306 #ifdef OWN_WINDOW
3307                         int wx = window.x, wy = window.y;
3308 #endif
3309
3310                         need_to_update = 0;
3311
3312                         update_text_area();
3313
3314 #ifdef OWN_WINDOW
3315                         if (own_window) {
3316                                 /* resize window if it isn't right size */
3317                                 if (!fixed_size &&
3318                                     (text_width + border_margin * 2 !=
3319                                      window.width
3320                                      || text_height + border_margin * 2 !=
3321                                      window.height)) {
3322                                         window.width =
3323                                             text_width +
3324                                             border_margin * 2 + 1;
3325                                         window.height =
3326                                             text_height +
3327                                             border_margin * 2 + 1;
3328                                         XResizeWindow(display,
3329                                                       window.window,
3330                                                       window.width,
3331                                                       window.height);
3332                                 }
3333
3334                                 /* move window if it isn't in right position */
3335                                 if (!fixed_pos
3336                                     && (window.x != wx
3337                                         || window.y != wy)) {
3338                                         XMoveWindow(display, window.window,
3339                                                     window.x, window.y);
3340                                 }
3341                         }
3342 #endif
3343
3344                         clear_text(1);
3345
3346 #ifdef XDBE
3347                         if (use_xdbe) {
3348                                 XRectangle r;
3349                                 r.x = text_start_x - border_margin;
3350                                 r.y = text_start_y - border_margin;
3351                                 r.width = text_width + border_margin * 2;
3352                                 r.height = text_height + border_margin * 2;
3353                                 XUnionRectWithRegion(&r, region, region);
3354                         }
3355 #endif
3356                 }
3357
3358                 /* handle X events */
3359
3360                 while (XPending(display)) {
3361                         XEvent ev;
3362                         XNextEvent(display, &ev);
3363
3364                         switch (ev.type) {
3365                         case Expose:
3366                                 {
3367                                         XRectangle r;
3368                                         r.x = ev.xexpose.x;
3369                                         r.y = ev.xexpose.y;
3370                                         r.width = ev.xexpose.width;
3371                                         r.height = ev.xexpose.height;
3372                                         XUnionRectWithRegion(&r, region,
3373                                                              region);
3374                                 }
3375                                 break;
3376
3377 #ifdef OWN_WINDOW
3378                         case ReparentNotify:
3379                                 /* set background to ParentRelative for all parents */
3380                                 if (own_window)
3381                                         set_transparent_background(window.
3382                                                                    window);
3383                                 break;
3384
3385                         case ConfigureNotify:
3386                                 if (own_window) {
3387                                         /* if window size isn't what expected, set fixed size */
3388                                         if (ev.xconfigure.width !=
3389                                             window.width
3390                                             || ev.xconfigure.height !=
3391                                             window.height) {
3392                                                 if (window.width != 0
3393                                                     && window.height != 0)
3394                                                         fixed_size = 1;
3395
3396                                                 /* clear old stuff before screwing up size and pos */
3397                                                 clear_text(1);
3398
3399                                                 {
3400                                                         XWindowAttributes
3401                                                             attrs;
3402                                                         if (XGetWindowAttributes(display, window.window, &attrs)) {
3403                                                                 window.
3404                                                                     width =
3405                                                                     attrs.
3406                                                                     width;
3407                                                                 window.
3408                                                                     height
3409                                                                     =
3410                                                                     attrs.
3411                                                                     height;
3412                                                         }
3413                                                 }
3414
3415                                                 text_width =
3416                                                     window.width -
3417                                                     border_margin * 2 - 1;
3418                                                 text_height =
3419                                                     window.height -
3420                                                     border_margin * 2 - 1;
3421                                         }
3422
3423                                         /* if position isn't what expected, set fixed pos, total_updates
3424                                          * avoids setting fixed_pos when window is set to weird locations
3425                                          * when started */
3426                                         if (total_updates >= 2
3427                                             && !fixed_pos
3428                                             && (window.x != ev.xconfigure.x
3429                                                 || window.y !=
3430                                                 ev.xconfigure.y)
3431                                             && (ev.xconfigure.x != 0
3432                                                 || ev.xconfigure.y != 0)) {
3433                                                 fixed_pos = 1;
3434                                         }
3435                                 }
3436                                 break;
3437 #endif
3438
3439                         default:
3440                                 break;
3441                         }
3442                 }
3443
3444                 /* XDBE doesn't seem to provide a way to clear the back buffer without
3445                  * interfering with the front buffer, other than passing XdbeBackground
3446                  * to XdbeSwapBuffers. That means that if we're using XDBE, we need to
3447                  * redraw the text even if it wasn't part of the exposed area. OTOH,
3448                  * if we're not going to call draw_stuff at all, then no swap happens
3449                  * and we can safely do nothing.
3450                  */
3451
3452                 if (!XEmptyRegion(region)) {
3453 #ifdef XDBE
3454                         if (use_xdbe) {
3455                                 XRectangle r;
3456                                 r.x = text_start_x - border_margin;
3457                                 r.y = text_start_y - border_margin;
3458                                 r.width = text_width + border_margin * 2;
3459                                 r.height = text_height + border_margin * 2;
3460                                 XUnionRectWithRegion(&r, region, region);
3461                         }
3462 #endif
3463                         XSetRegion(display, window.gc, region);
3464 #ifdef XFT
3465                         if (use_xft)
3466                                 XftDrawSetClip(window.xftdraw, region);
3467 #endif
3468                         draw_stuff();
3469                         XDestroyRegion(region);
3470                         region = XCreateRegion();
3471                 }
3472         }
3473 }
3474
3475 static void load_config_file(const char *);
3476
3477 /* signal handler that reloads config file */
3478 static void reload_handler(int a)
3479 {
3480         ERR("Conky: received signal %d, reloading config\n", a);
3481
3482         if (current_config) {
3483                 clear_fs_stats();
3484                 load_config_file(current_config);
3485                 load_fonts();
3486                 set_font();
3487                 extract_variable_text(text);
3488                 free(text);
3489                 text = NULL;
3490                 update_text();
3491         }
3492 }
3493
3494 static void clean_up()
3495 {
3496 #ifdef XDBE
3497         if (use_xdbe)
3498                 XdbeDeallocateBackBufferName(display, window.back_buffer);
3499 #endif
3500 #ifdef OWN_WINDOW
3501         if (own_window)
3502                 XDestroyWindow(display, window.window);
3503         else
3504 #endif
3505         {
3506                 clear_text(1);
3507                 XFlush(display);
3508         }
3509
3510         XFreeGC(display, window.gc);
3511
3512         /* it is really pointless to free() memory at the end of program but ak|ra
3513          * wants me to do this */
3514
3515         free_text_objects();
3516
3517         if (text != original_text)
3518                 free(text);
3519
3520         free(current_config);
3521         free(current_mail_spool);
3522 #ifdef SETI
3523         free(seti_dir);
3524 #endif
3525 }
3526
3527 static void term_handler(int a)
3528 {
3529         a = a;                  /* to get rid of warning */
3530         clean_up();
3531         exit(0);
3532 }
3533
3534 static int string_to_bool(const char *s)
3535 {
3536         if (!s)
3537                 return 1;
3538         if (strcasecmp(s, "yes") == 0)
3539                 return 1;
3540         if (strcasecmp(s, "true") == 0)
3541                 return 1;
3542         if (strcasecmp(s, "1") == 0)
3543                 return 1;
3544         return 0;
3545 }
3546
3547 static enum alignment string_to_alignment(const char *s)
3548 {
3549         if (strcasecmp(s, "top_left") == 0)
3550                 return TOP_LEFT;
3551         else if (strcasecmp(s, "top_right") == 0)
3552                 return TOP_RIGHT;
3553         else if (strcasecmp(s, "bottom_left") == 0)
3554                 return BOTTOM_LEFT;
3555         else if (strcasecmp(s, "bottom_right") == 0)
3556                 return BOTTOM_RIGHT;
3557         else if (strcasecmp(s, "tl") == 0)
3558                 return TOP_LEFT;
3559         else if (strcasecmp(s, "tr") == 0)
3560                 return TOP_RIGHT;
3561         else if (strcasecmp(s, "bl") == 0)
3562                 return BOTTOM_LEFT;
3563         else if (strcasecmp(s, "br") == 0)
3564                 return BOTTOM_RIGHT;
3565
3566         return TOP_LEFT;
3567 }
3568
3569 static void set_default_configurations(void)
3570 {
3571         text_alignment = BOTTOM_LEFT;
3572         fork_to_background = 0;
3573         border_margin = 3;
3574         border_width = 1;
3575         total_run_times = 0;
3576         info.cpu_avg_samples = 2;
3577         info.net_avg_samples = 2;
3578         info.memmax = 0;
3579         top_cpu = 0;
3580         top_mem = 0;
3581 #ifdef MPD
3582         strcpy(info.mpd.host, "localhost");
3583         info.mpd.port = 6600;
3584         info.mpd.status = "Checking status...";
3585 #endif
3586         out_to_console = 0;
3587         use_spacer = 0;
3588         default_fg_color = WhitePixel(display, screen);
3589         default_bg_color = BlackPixel(display, screen);
3590         default_out_color = BlackPixel(display, screen);
3591         draw_borders = 0;
3592         draw_shades = 1;
3593         draw_outline = 0;
3594 /*#ifdef XFT
3595         use_xft = 1;
3596         set_first_font("courier-12");
3597 #endif
3598 #else*/
3599         set_first_font("6x10");
3600         gap_x = 5;
3601         gap_y = 5;
3602
3603         free(current_mail_spool);
3604         {
3605                 char buf[256];
3606                 variable_substitute(MAIL_FILE, buf, 256);
3607                 if (buf[0] != '\0')
3608                         current_mail_spool = strdup(buf);
3609         }
3610
3611         minimum_width = 5;
3612         minimum_height = 5;
3613         no_buffers = 1;
3614 #ifdef OWN_WINDOW
3615         own_window = 0;
3616 #endif
3617         stippled_borders = 0;
3618         update_interval = 10.0;
3619         stuff_in_upper_case = 0;
3620 #ifdef MLDONKEY
3621         mlconfig.mldonkey_hostname = "127.0.0.1";
3622         mlconfig.mldonkey_port = 4001;
3623         mlconfig.mldonkey_login = NULL;
3624         mlconfig.mldonkey_password = NULL;
3625 #endif
3626 #ifdef METAR
3627         metar_station = NULL;
3628         metar_server = NULL;
3629         metar_path = NULL;
3630         last_metar_update = 0;
3631 #endif
3632 }
3633
3634 static void load_config_file(const char *f)
3635 {
3636 #define CONF_ERR ERR("%s: %d: config file error", f, line)
3637         int line = 0;
3638         FILE *fp;
3639
3640         set_default_configurations();
3641
3642         fp = open_file(f, 0);
3643         if (!fp)
3644                 return;
3645
3646         while (!feof(fp)) {
3647                 char buf[256], *p, *p2, *name, *value;
3648                 line++;
3649                 if (fgets(buf, 256, fp) == NULL)
3650                         break;
3651
3652                 p = buf;
3653
3654                 /* break at comment */
3655                 p2 = strchr(p, '#');
3656                 if (p2)
3657                         *p2 = '\0';
3658
3659                 /* skip spaces */
3660                 while (*p && isspace((int) *p))
3661                         p++;
3662                 if (*p == '\0')
3663                         continue;       /* empty line */
3664
3665                 name = p;
3666
3667                 /* skip name */
3668                 p2 = p;
3669                 while (*p2 && !isspace((int) *p2))
3670                         p2++;
3671                 if (*p2 != '\0') {
3672                         *p2 = '\0';     /* break at name's end */
3673                         p2++;
3674                 }
3675
3676                 /* get value */
3677                 if (*p2) {
3678                         p = p2;
3679                         while (*p && isspace((int) *p))
3680                                 p++;
3681
3682                         value = p;
3683
3684                         p2 = value + strlen(value);
3685                         while (isspace((int) *(p2 - 1)))
3686                                 *--p2 = '\0';
3687                 } else {
3688                         value = 0;
3689                 }
3690
3691 #define CONF2(a) if (strcasecmp(name, a) == 0)
3692 #define CONF(a) else CONF2(a)
3693 #define CONF3(a,b) \
3694 else if (strcasecmp(name, a) == 0 || strcasecmp(name, a) == 0)
3695
3696
3697                 CONF2("alignment") {
3698                         if (value) {
3699                                 int a = string_to_alignment(value);
3700                                 if (a <= 0)
3701                                         CONF_ERR;
3702                                 else
3703                                         text_alignment = a;
3704                         } else
3705                                 CONF_ERR;
3706                 }
3707                 CONF("background") {
3708                         fork_to_background = string_to_bool(value);
3709                 }
3710                 CONF("border_margin") {
3711                         if (value)
3712                                 border_margin = strtol(value, 0, 0);
3713                         else
3714                                 CONF_ERR;
3715                 }
3716                 CONF("border_width") {
3717                         if (value)
3718                                 border_width = strtol(value, 0, 0);
3719                         else
3720                                 CONF_ERR;
3721                 }
3722                 CONF("default_color") {
3723                         if (value)
3724                                 default_fg_color = get_x11_color(value);
3725                         else
3726                                 CONF_ERR;
3727                 }
3728                 CONF3("default_shade_color", "default_shadecolor") {
3729                         if (value)
3730                                 default_bg_color = get_x11_color(value);
3731                         else
3732                                 CONF_ERR;
3733                 }
3734                 CONF3("default_outline_color", "default_outlinecolor") {
3735                         if (value)
3736                                 default_out_color = get_x11_color(value);
3737                         else
3738                                 CONF_ERR;
3739                 }
3740 #ifdef MPD
3741                 CONF("mpd_host") {
3742                         if (value)
3743                                 strcpy(info.mpd.host, value);
3744                         else
3745                                 CONF_ERR;
3746                 }
3747                 CONF("mpd_port") {
3748                         if (value) {
3749                                 info.mpd.port = strtol(value, 0, 0);
3750                                 if (info.mpd.port < 1
3751                                     || info.mpd.port > 0xffff)
3752                                         CONF_ERR;
3753                         }
3754                 }
3755 #endif
3756                 CONF("cpu_avg_samples") {
3757                         if (value) {
3758                                 cpu_avg_samples = strtol(value, 0, 0);
3759                                 if (cpu_avg_samples < 1
3760                                     || cpu_avg_samples > 14)
3761                                         CONF_ERR;
3762                                 else
3763                                         info.
3764                                             cpu_avg_samples
3765                                             = cpu_avg_samples;
3766                         } else
3767                                 CONF_ERR;
3768                 }
3769                 CONF("net_avg_samples") {
3770                         if (value) {
3771                                 net_avg_samples = strtol(value, 0, 0);
3772                                 if (net_avg_samples < 1
3773                                     || net_avg_samples > 14)
3774                                         CONF_ERR;
3775                                 else
3776                                         info.
3777                                             net_avg_samples
3778                                             = net_avg_samples;
3779                         } else
3780                                 CONF_ERR;
3781                 }
3782
3783
3784
3785
3786
3787
3788                 CONF("override_utf8_locale") {
3789                         utf8_mode = string_to_bool(value);
3790                 }
3791 #ifdef XDBE
3792                 CONF("double_buffer") {
3793                         use_xdbe = string_to_bool(value);
3794                 }
3795 #endif
3796                 CONF("draw_borders") {
3797                         draw_borders = string_to_bool(value);
3798                 }
3799                 CONF("draw_shades") {
3800                         draw_shades = string_to_bool(value);
3801                 }
3802                 CONF("draw_outline") {
3803                         draw_outline = string_to_bool(value);
3804                 }
3805                 CONF("out_to_console") {
3806                         out_to_console = string_to_bool(value);
3807                 }
3808                 CONF("use_spacer") {
3809                         use_spacer = string_to_bool(value);
3810                 }
3811 #ifdef XFT
3812                 CONF("use_xft") {
3813                         use_xft = string_to_bool(value);
3814                 }
3815                 CONF("font") {
3816                         if (!use_xft) {
3817                                 if (value) {
3818                                         set_first_font(value);
3819                                 } else
3820                                         CONF_ERR;
3821                         }
3822                 }
3823                 CONF("xftalpha") {
3824                         if (value && font_count >= 0)
3825                                 fonts[0].font_alpha = atof(value)
3826                                     * 65535.0;
3827                         else
3828                                 CONF_ERR;
3829                 }
3830                 CONF("xftfont") {
3831 #else
3832                 CONF("use_xft") {
3833                         if (string_to_bool(value))
3834                                 ERR("Xft not enabled");
3835                 }
3836                 CONF("xftfont") {
3837                         /* xftfont silently ignored when no Xft */
3838                 }
3839                 CONF("xftalpha") {
3840                         /* xftalpha is silently ignored when no Xft */
3841                 }
3842                 CONF("font") {
3843 #endif
3844                         if (value) {
3845                                 set_first_font(value);
3846                         } else
3847                                 CONF_ERR;
3848                 }
3849                 CONF("gap_x") {
3850                         if (value)
3851                                 gap_x = atoi(value);
3852                         else
3853                                 CONF_ERR;
3854                 }
3855                 CONF("gap_y") {
3856                         if (value)
3857                                 gap_y = atoi(value);
3858                         else
3859                                 CONF_ERR;
3860                 }
3861                 CONF("mail_spool") {
3862                         if (value) {
3863                                 char buf[256];
3864                                 variable_substitute(value, buf, 256);
3865
3866                                 if (buf[0]
3867                                     != '\0') {
3868                                         if (current_mail_spool)
3869                                                 free(current_mail_spool);
3870                                         current_mail_spool = strdup(buf);
3871                                 }
3872                         } else
3873                                 CONF_ERR;
3874                 }
3875                 CONF("minimum_size") {
3876                         if (value) {
3877                                 if (sscanf
3878                                     (value, "%d %d", &minimum_width,
3879                                      &minimum_height) != 2)
3880                                         if (sscanf
3881                                             (value, "%d",
3882                                              &minimum_width) != 1)
3883                                                 CONF_ERR;
3884                         } else
3885                                 CONF_ERR;
3886                 }
3887                 CONF("no_buffers") {
3888                         no_buffers = string_to_bool(value);
3889                 }
3890 #ifdef MLDONKEY
3891                 CONF("mldonkey_hostname") {
3892                         if (value) {
3893                                 if (mlconfig.mldonkey_hostname != NULL) {
3894                                         free(mlconfig.mldonkey_hostname);
3895                                 }
3896                         mlconfig.mldonkey_hostname = strdup(value);
3897                         }
3898                         else
3899                                 CONF_ERR;
3900                 }
3901                 CONF("mldonkey_port") {
3902                         if (value)
3903                                 mlconfig.mldonkey_port = atoi(value);
3904                         else
3905                                 CONF_ERR;
3906                 }
3907                 CONF("mldonkey_login") {
3908                         if (value) {
3909                                 if (mlconfig.mldonkey_login != NULL) {
3910                                         free(mlconfig.mldonkey_login);
3911                                 }
3912                                 mlconfig.mldonkey_login = strdup(value);
3913                         }
3914                         else
3915                                 CONF_ERR;
3916                 }
3917                 CONF("mldonkey_password") {
3918                         if (value) {
3919                                 if (mlconfig.mldonkey_password != NULL) {
3920                                         free(mlconfig.mldonkey_password);
3921                                 }
3922                                 mlconfig.mldonkey_password = strdup(value);
3923                         }
3924                         else
3925                                 CONF_ERR;
3926                 }
3927 #endif
3928 #ifdef OWN_WINDOW
3929                 CONF("own_window") {
3930                         own_window = string_to_bool(value);
3931                 }
3932 #endif
3933                 CONF("pad_percents") {
3934                         pad_percents = atoi(value);
3935                 }
3936                 CONF("stippled_borders") {
3937                         if (value)
3938                                 stippled_borders = strtol(value, 0, 0);
3939                         else
3940                                 stippled_borders = 4;
3941                 }
3942                 CONF("temp1") {
3943                         ERR("temp1 configuration is obsolete, use ${i2c <i2c device here> temp 1}");
3944                 }
3945                 CONF("temp1") {
3946                         ERR("temp2 configuration is obsolete, use ${i2c <i2c device here> temp 2}");
3947                 }
3948                 CONF("update_interval") {
3949                         if (value)
3950                                 update_interval = strtod(value, 0);
3951                         else
3952                                 CONF_ERR;
3953                 }
3954                 CONF("total_run_times") {
3955                         if (value)
3956                                 total_run_times = strtod(value, 0);
3957                         else
3958                                 CONF_ERR;
3959                 }
3960                 CONF("uppercase") {
3961                         stuff_in_upper_case = string_to_bool(value);
3962                 }
3963 #ifdef SETI
3964                 CONF("seti_dir") {
3965                         seti_dir = (char *)
3966                             malloc(strlen(value)
3967                                    + 1);
3968                         strcpy(seti_dir, value);
3969                 }
3970 #endif
3971 #ifdef METAR
3972                 CONF("metar_station") {
3973                         metar_station = (char *)
3974                             malloc(strlen(value)
3975                                    + 5);
3976                         strcpy(metar_station, value);
3977                         strcat(metar_station, ".TXT");
3978                 }
3979                 CONF("metar_server") {
3980                         metar_server = (char *)
3981                             malloc(strlen(value)
3982                                    + 1);
3983                         strcpy(metar_server, value);
3984                 }
3985                 CONF("metar_path") {
3986                         metar_path = (char *)
3987                             malloc(strlen(value)
3988                                    + 1);
3989                         strcpy(metar_path, value);
3990
3991                 }
3992 #endif
3993                 CONF("text") {
3994                         if (text != original_text)
3995                                 free(text);
3996
3997                         text = (char *)
3998                             malloc(1);
3999                         text[0]
4000                             = '\0';
4001
4002                         while (!feof(fp)) {
4003                                 unsigned
4004                                 int l = strlen(text);
4005                                 if (fgets(buf, 256, fp) == NULL)
4006                                         break;
4007                                 text = (char *)
4008                                     realloc(text, l + strlen(buf)
4009                                             + 1);
4010                                 strcat(text, buf);
4011
4012                                 if (strlen(text) > 1024 * 8)
4013                                         break;
4014                         }
4015                         fclose(fp);
4016                         return;
4017                 }
4018                 else
4019                 ERR("%s: %d: no such configuration: '%s'", f, line, name);
4020
4021 #undef CONF
4022 #undef CONF2
4023         }
4024
4025         fclose(fp);
4026 #undef CONF_ERR
4027 }
4028
4029                                                                                                                                                                                         /* : means that character before that takes an argument */
4030 static
4031     const
4032     char
4033 *getopt_string = "vVdt:f:u:i:hc:w:x:y:a:"
4034 #ifdef OWN_WINDOW
4035     "o"
4036 #endif
4037 #ifdef XDBE
4038     "b"
4039 #endif
4040     ;
4041
4042
4043 int main(int argc, char **argv)
4044 {
4045         /* handle command line parameters that don't change configs */
4046         char *s;
4047         char temp[10];
4048         unsigned int x;
4049
4050         if (((s = getenv("LC_ALL")) && *s) || ((s = getenv("LC_CTYPE")) && 
4051                      *s) || ((s = getenv("LANG")) && *s)) {
4052                 strcpy(temp, s);
4053                 for(x = 0; x < strlen(s) ; x++) {
4054                         temp[x] = tolower(s[x]);
4055                 }
4056                 if (strstr(temp, "utf-8") || strstr(temp, "utf8")) {
4057                         utf8_mode = 1;
4058                 }
4059         }
4060         if (!setlocale(LC_CTYPE, "")) {
4061                 ERR("Can't set the specified locale!\nCheck LANG, LC_CTYPE, LC_ALL.");
4062                 return 1;
4063         }
4064         while (1) {
4065                 int c = getopt(argc,
4066                                argv,
4067                                getopt_string);
4068                 if (c == -1)
4069                         break;
4070
4071                 switch (c) {
4072                 case 'v':
4073                 case 'V':
4074                         printf
4075                             ("Conky " VERSION " compiled " __DATE__ "\n");
4076                         return 0;
4077
4078                 case 'c':
4079                         /* if current_config is set to a strdup of CONFIG_FILE, free it (even
4080                          * though free() does the NULL check itself;), then load optarg value */
4081                         if (current_config)
4082                                 free(current_config);
4083                         current_config = strdup(optarg);
4084                         break;
4085
4086                 case 'h':
4087                         printf
4088                             ("Usage: %s [OPTION]...\n"
4089                              "Conky is a system monitor that renders text on desktop or to own transparent\n"
4090                              "window. Command line options will override configurations defined in config\n"
4091                              "file.\n"
4092                              "   -V            version\n"
4093                              "   -a ALIGNMENT  text alignment on screen, {top,bottom}_{left,right}\n"
4094                              "   -c FILE       config file to load instead of "
4095                              CONFIG_FILE
4096                              "\n"
4097                              "   -d            daemonize, fork to background\n"
4098                              "   -f FONT       font to use\n"
4099                              "   -h            help\n"
4100 #ifdef OWN_WINDOW
4101                              "   -o            create own window to draw\n"
4102 #endif
4103 #ifdef XDBE
4104                              "   -b            double buffer (prevents flickering)\n"
4105 #endif
4106                              "   -t TEXT       text to render, remember single quotes, like -t '$uptime'\n"
4107                              "   -u SECS       update interval\n"
4108                              "   -i NUM        number of times to update Conky\n"
4109                              "   -w WIN_ID     window id to draw\n"
4110                              "   -x X          x position\n"
4111                              "   -y Y          y position\n", argv[0]);
4112                         return 0;
4113
4114                 case 'w':
4115                         window.window = strtol(optarg, 0, 0);
4116                         break;
4117
4118                 case '?':
4119                         exit(EXIT_FAILURE);
4120                 }
4121         }
4122         /* initalize X BEFORE we load config. (we need to so that 'screen' is set) */
4123         init_X11();
4124
4125         tmpstring1 = (char *)
4126             malloc(TEXT_BUFFER_SIZE);
4127         tmpstring2 = (char *)
4128             malloc(TEXT_BUFFER_SIZE);
4129
4130         /* load current_config or CONFIG_FILE */
4131
4132 #ifdef CONFIG_FILE
4133         if (current_config == NULL) {
4134                 /* load default config file */
4135                 char buf[256];
4136
4137                 variable_substitute(CONFIG_FILE, buf, 256);
4138
4139                 if (buf[0] != '\0')
4140                         current_config = strdup(buf);
4141         }
4142 #endif
4143
4144         if (current_config != NULL)
4145                 load_config_file(current_config);
4146         else
4147                 set_default_configurations();
4148
4149 #ifdef MAIL_FILE
4150         if (current_mail_spool == NULL) {
4151                 char buf[256];
4152                 variable_substitute(MAIL_FILE, buf, 256);
4153
4154                 if (buf[0] != '\0')
4155                         current_mail_spool = strdup(buf);
4156         }
4157 #endif
4158
4159         /* handle other command line arguments */
4160
4161         optind = 0;
4162
4163         while (1) {
4164                 int c = getopt(argc,
4165                                argv,
4166                                getopt_string);
4167                 if (c == -1)
4168                         break;
4169
4170                 switch (c) {
4171                 case 'a':
4172                         text_alignment = string_to_alignment(optarg);
4173                         break;
4174
4175                 case 'd':
4176                         fork_to_background = 1;
4177                         break;
4178
4179                 case 'f':
4180                         set_first_font(optarg);
4181                         break;
4182
4183 #ifdef OWN_WINDOW
4184                 case 'o':
4185                         own_window = 1;
4186                         break;
4187 #endif
4188 #ifdef XDBE
4189                 case 'b':
4190                         use_xdbe = 1;
4191                         break;
4192 #endif
4193
4194                 case 't':
4195                         if (text != original_text)
4196                                 free(text);
4197                         text = strdup(optarg);
4198                         convert_escapes(text);
4199                         break;
4200
4201                 case 'u':
4202                         update_interval = strtod(optarg, 0);
4203                         break;
4204
4205                 case 'i':
4206                         total_run_times = strtod(optarg, 0);
4207                         break;
4208
4209                 case 'x':
4210                         gap_x = atoi(optarg);
4211                         break;
4212
4213                 case 'y':
4214                         gap_y = atoi(optarg);
4215                         break;
4216
4217                 case '?':
4218                         exit(EXIT_FAILURE);
4219                 }
4220         }
4221
4222         /* load font */
4223         load_fonts();
4224
4225         /* generate text and get initial size */
4226         extract_variable_text(text);
4227         if (text != original_text) {
4228                 free(text);
4229         }
4230         text = NULL;
4231
4232         update_uname();
4233
4234         generate_text();
4235         update_text_area();     /* to get initial size of the window */
4236
4237         init_window
4238             (own_window,
4239              text_width
4240              + border_margin * 2 + 1, text_height + border_margin * 2 + 1);
4241
4242         update_text_area();     /* to position text/window on screen */
4243
4244 #ifdef CAIRO
4245 // why the fuck not?
4246 //do_it();
4247 #endif
4248
4249 #ifdef OWN_WINDOW
4250         if (own_window)
4251                 XMoveWindow(display, window.window, window.x, window.y);
4252 #endif
4253
4254         create_gc();
4255
4256         set_font();
4257
4258         draw_stuff();
4259
4260         /* fork */
4261         if (fork_to_background) {
4262                 int ret = fork();
4263                 switch (ret) {
4264                 case -1:
4265                         ERR("can't fork() to background: %s",
4266                             strerror(errno));
4267                         break;
4268
4269                 case 0:
4270                         break;
4271
4272                 default:
4273                         fprintf
4274                             (stderr,
4275                              "Conky: forked to background, pid is %d\n",
4276                              ret);
4277                         return 0;
4278                 }
4279         }
4280
4281         /* set SIGUSR1, SIGINT and SIGTERM handlers */
4282         {
4283                 struct
4284                 sigaction sa;
4285
4286                 sa.sa_handler = reload_handler;
4287                 sigemptyset(&sa.sa_mask);
4288                 sa.sa_flags = SA_RESTART;
4289                 if (sigaction(SIGUSR1, &sa, NULL) != 0)
4290                         ERR("can't set signal handler for SIGUSR1: %s",
4291                             strerror(errno));
4292
4293                 sa.sa_handler = term_handler;
4294                 sigemptyset(&sa.sa_mask);
4295                 sa.sa_flags = SA_RESTART;
4296                 if (sigaction(SIGINT, &sa, NULL) != 0)
4297                         ERR("can't set signal handler for SIGINT: %s",
4298                             strerror(errno));
4299
4300                 sa.sa_handler = term_handler;
4301                 sigemptyset(&sa.sa_mask);
4302                 sa.sa_flags = SA_RESTART;
4303                 if (sigaction(SIGTERM, &sa, NULL) != 0)
4304                         ERR("can't set signal handler for SIGTERM: %s",
4305                             strerror(errno));
4306         }
4307         main_loop();
4308         free(tmpstring1);
4309         free(tmpstring2);
4310         return 0;
4311 }