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