Make a description of ${cpu} variable not so confusing.
[monky] / src / conky.c
index 16231e5..f9188c3 100644 (file)
 #include <sys/time.h>
 #ifdef X11
 #include <X11/Xutil.h>
+#include <X11/extensions/Xdamage.h>
+#ifdef IMLIB2
+#include <Imlib2.h>
+#endif /* IMLIB2 */
 #endif /* X11 */
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#include "build.h"
 
 #define CONFIG_FILE "$HOME/.conkyrc"
 #define MAIL_FILE "$MAIL"
 
 #ifdef X11
 
+/*
+ * text size
+ */
+
+static int text_start_x, text_start_y; /* text start position in window */
+static int text_width, text_height;
+
 /* alignments */
 enum alignment {
        TOP_LEFT = 1,
@@ -68,10 +88,10 @@ struct font_list *fonts = NULL;
 
 #ifdef XFT
 
-#define font_height() use_xft ? (fonts[selected_font].xftfont->ascent + fonts[selected_font].xftfont->descent) : \
-(fonts[selected_font].font->max_bounds.ascent + fonts[selected_font].font->max_bounds.descent)
-#define font_ascent() use_xft ? fonts[selected_font].xftfont->ascent : fonts[selected_font].font->max_bounds.ascent
-#define font_descent() use_xft ? fonts[selected_font].xftfont->descent : fonts[selected_font].font->max_bounds.descent
+#define font_height() (use_xft ? (fonts[selected_font].xftfont->ascent + fonts[selected_font].xftfont->descent) : \
+(fonts[selected_font].font->max_bounds.ascent + fonts[selected_font].font->max_bounds.descent))
+#define font_ascent() (use_xft ? fonts[selected_font].xftfont->ascent : fonts[selected_font].font->max_bounds.ascent)
+#define font_descent() (use_xft ? fonts[selected_font].xftfont->descent : fonts[selected_font].font->max_bounds.descent)
 
 #else
 
@@ -86,6 +106,65 @@ struct font_list *fonts = NULL;
 
 static void set_font();
 
+static void print_version()
+{
+       printf("Conky %s compiled %s for %s\n",
+                       VERSION, BUILD_DATE, BUILD_ARCH);
+
+       printf(
+       "\nCompiled in features:\n\n"
+#ifdef X11
+       " X11:\n"
+# ifdef XFT
+       "  * xft\n"
+# endif /* XFT */
+# ifdef HAVE_XDAMAGE
+       "  * Xdamage extension\n"
+# endif /* HAVE_XDAMAGE */
+# ifdef HAVE_XDBE
+       "  * Xdbe extension (double buffer)\n"
+# endif /* HAVE_XDBE */
+#endif /* X11 */
+       "\n Music detection:\n"
+#ifdef XMMS
+       "  * xmms\n"
+#endif /* XMMS */
+#ifdef BMP
+       "  * bmp\n"
+#endif /* BMP */
+#ifdef AUDACIOUS
+       "  * audacious\n"
+#endif /* AUDACIOUS */
+#ifdef INFOPIPE
+       "  * infopipe\n"
+#endif /* INFOPIPE */
+#ifdef BMPX
+       "  * bmpx\n"
+#endif /* BMPX */
+#ifdef XMMS2
+       "  * xmms2\n"
+#endif /* XMMS2 */
+#ifdef MPD
+       "  * mpd\n"
+#endif /* MPD */
+       "\n General features:\n"
+#ifdef TCP_PORT_MONITOR
+       "  * portmon\n"
+#endif /* TCP_PORT_MONITOR */
+#ifdef MLDONKEY
+       "  * mldonkey\n"
+#endif /* MLDONKEY */
+#ifdef HDDTEMP
+        "  * hddtemp\n"
+#endif /* HDDTEMP */
+#ifdef SETI
+       "  * seti\n"
+#endif /* SETI*/
+       "\n");  
+
+       exit(0);
+}
+
 int addfont(const char *data_in)
 {
        if (font_count > MAX_FONTS) {
@@ -155,7 +234,7 @@ void free_fonts()
 static void load_fonts()
 {
        int i;
-       for (i=0;i<=font_count;i++) {
+       for (i=0; i <= font_count; i++) {
 #ifdef XFT
        /* load Xft font */
        if (use_xft) {
@@ -249,6 +328,51 @@ static int fixed_size = 0, fixed_pos = 0;
 static int minimum_width, minimum_height;
 static int maximum_width;
 
+
+
+#ifdef HAVE_ICONV
+#define CODEPAGE_LENGTH 20
+long iconv_selected;
+long iconv_count;
+char iconv_converting;
+static iconv_t **iconv_cd = 0;
+
+int register_iconv(iconv_t *new_iconv)
+{
+       if (iconv_cd) {
+               iconv_cd = realloc(iconv, sizeof(iconv_t *) * (iconv_count + 1));
+       } else {
+               iconv_cd = malloc(sizeof(iconv_t *));
+       }
+       if (!iconv_cd) {
+               CRIT_ERR("Out of memory");
+       }
+       iconv_cd[iconv_count] = malloc(sizeof(iconv_t));
+       if (!iconv_cd[iconv_count]) {
+               CRIT_ERR("Out of memory");
+       }
+       memcpy(iconv_cd[iconv_count], new_iconv, sizeof(iconv_t));
+       iconv_count++;
+       return iconv_count;
+}
+
+void free_iconv(void)
+{
+       if (iconv_cd) {
+               long i;
+               for (i = iconv_count; i < 0; i++) {
+                       if (iconv_cd[i]) {
+                               free(iconv_cd[i]);
+                       }
+               }
+               free(iconv_cd);
+       }
+       iconv_cd = 0;
+               
+}
+
+#endif
+
 /* UTF-8 */
 int utf8_mode = 0;
 
@@ -303,6 +427,7 @@ static char original_text[] =
     "${tail /var/log/Xorg.0.log 3}";
 
 static char *text = original_text;
+long text_lines;
 
 static int total_updates;
 
@@ -386,6 +511,8 @@ enum {
        OFFSET,
        VOFFSET,
        FONT,
+       GOTO,
+       TAB,
 };
 
 static struct special_t {
@@ -497,20 +624,24 @@ static char *scan_font(const char *args)
        if (args && sizeof(args) < 127) {
                return strdup(args);
        }
-       else {
-               ERR("font scan failed, lets hope it doesn't mess stuff up");
-       }
        return NULL;
 }
 
 #ifdef X11
 static void new_font(char *buf, char * args) {
-       struct special_t *s = new_special(buf, FONT);
-       if (!s->font_added || strcmp(args, fonts[s->font_added].name)) {
+       if (args) {
+               struct special_t *s = new_special(buf, FONT);
+               if (!s->font_added || strcmp(args, fonts[s->font_added].name)) {
+                       int tmp = selected_font;
+                       selected_font = s->font_added = addfont(args);
+                       load_fonts();
+                       selected_font = tmp;
+               }
+       } else {
+               struct special_t *s = new_special(buf, FONT);
                int tmp = selected_font;
-               selected_font = s->font_added = addfont(args);
+               selected_font = s->font_added = 0;
                load_fonts();
-//             set_font();
                selected_font = tmp;
        }
 }
@@ -565,9 +696,9 @@ static void new_graph(char *buf, int w, int h, unsigned int first_colour, unsign
        s->width = w;
        if (s->graph == NULL) {
                if (s->width > 0 && s->width < MAX_GRAPH_DEPTH) {
-                       s->graph_width = s->width - 3;  // subtract 3 for the box
+                       s->graph_width = s->width/* - 2*/;      // subtract 2 for the box
                } else {
-                       s->graph_width = MAX_GRAPH_DEPTH - 3;
+                       s->graph_width = MAX_GRAPH_DEPTH - 2;
                }
                s->graph = malloc(s->graph_width * sizeof(double));
                memset(s->graph, 0, s->graph_width * sizeof(double));
@@ -582,7 +713,7 @@ static void new_graph(char *buf, int w, int h, unsigned int first_colour, unsign
                s->scaled = 1;
        }
        /*if (s->width) {
-               s->graph_width = s->width - 3;  // subtract 3 for rectangle around
+               s->graph_width = s->width - 2;  // subtract 2 for rectangle around
        }*/
        if (s->scaled) {
                s->graph_scale = 1;
@@ -684,6 +815,17 @@ static inline void new_alignc(char *buf, long c)
        new_special(buf, ALIGNC)->arg = c;
 }
 
+static inline void new_goto(char *buf, long c)
+{
+       new_special(buf, GOTO)->arg = c;
+}
+
+static inline void new_tab(char *buf, int a, int b) {
+       struct special_t *s = new_special(buf, TAB);
+       s->width = a;
+       s->arg = b;
+}
+
 /* quite boring functions */
 
 static inline void for_each_line(char *b, void (*f) (char *))
@@ -726,21 +868,21 @@ static void human_readable(long long a, char *buf, int size)
 {
        // Strange conditional due to possible overflows
        if(a / 1024 / 1024 / 1024.0 > 1024.0){
-               snprintf(buf, size, "%.2fT", (a / 1024 / 1024 / 1024) / 1024.0);
+               snprintf(buf, size, "%.2fTiB", (a / 1024 / 1024 / 1024) / 1024.0);
        }
        else if (a >= 1024 * 1024 * 1024) {
-               snprintf(buf, size, "%.2fG", (a / 1024 / 1024) / 1024.0);
+               snprintf(buf, size, "%.2fGiB", (a / 1024 / 1024) / 1024.0);
        }
        else if (a >= 1024 * 1024) {
                double m = (a / 1024) / 1024.0;
                if (m >= 100.0)
-                       snprintf(buf, size, "%.0fM", m);
+                       snprintf(buf, size, "%.0fMiB", m);
                else
-                       snprintf(buf, size, "%.1fM", m);
+                       snprintf(buf, size, "%.1fMiB", m);
        } else if (a >= 1024)
-               snprintf(buf, size, "%Ldk", a / (long long) 1024);
+               snprintf(buf, size, "%LdKiB", a / (long long) 1024);
        else
-               snprintf(buf, size, "%Ld", a);
+               snprintf(buf, size, "%LdB", a);
 }
 
 /* text handling */
@@ -769,6 +911,7 @@ enum text_object_type {
        OBJ_downspeedgraph,
        OBJ_else,
        OBJ_endif,
+       OBJ_image,
        OBJ_exec,
        OBJ_execi,
        OBJ_texeci,
@@ -787,6 +930,8 @@ enum text_object_type {
        OBJ_fs_size,
        OBJ_fs_used,
        OBJ_fs_used_perc,
+       OBJ_goto,
+       OBJ_tab,
        OBJ_hr,
        OBJ_offset,
        OBJ_voffset,
@@ -810,6 +955,8 @@ enum text_object_type {
        OBJ_ibm_volume,
        OBJ_ibm_brightness,
         OBJ_pb_battery,
+       OBJ_voltage_mv,
+       OBJ_voltage_v,
 #endif /* __linux__ */
        OBJ_if_existing,
        OBJ_if_mounted,
@@ -863,6 +1010,7 @@ enum text_object_type {
        OBJ_text,
        OBJ_time,
        OBJ_utime,
+       OBJ_tztime,
        OBJ_totaldown,
        OBJ_totalup,
        OBJ_updates,
@@ -955,11 +1103,20 @@ enum text_object_type {
 #ifdef TCP_PORT_MONITOR
        OBJ_tcp_portmon,
 #endif
+
+#ifdef HAVE_ICONV
+       OBJ_iconv_start,
+       OBJ_iconv_stop,
+#endif
+#ifdef HDDTEMP
+       OBJ_hddtemp,
+#endif
 };
 
 struct text_object {
        int type;
        int a, b;
+       long line;
        unsigned int c, d, e;
        float f;
        char global_mode;
@@ -973,6 +1130,12 @@ struct text_object {
                unsigned char loadavg[3];
                unsigned int cpu_index;
                struct mail_s *mail;
+
+               struct {
+                       char *tz;    /* timezone variable */
+                       char *fmt;   /* time display formatting */
+               } tztime;
+
                struct {
                        struct fs_stat *fs;
                        int w, h;
@@ -1028,6 +1191,11 @@ struct text_object {
                        int        connection_index;  /* 0 to n-1 connections. */
                } tcp_port_monitor;
 #endif
+               struct {
+                       char *addr;
+                       int port;
+                       char *dev;
+               } hddtemp; /* 2 */
        } data;
 };
 
@@ -1053,26 +1221,20 @@ int register_thread(struct thread_info_s *new_thread)
        } else {
                thread_list[thread_count] = new_thread;
                thread_count++;
+               // may as well fix the mutex for them as well
+               pthread_mutex_init(&(new_thread->mutex), NULL);
        }
        return thread_count - 1;
 }
 
-void replace_thread(struct thread_info_s *new_thread, int pos) // this isn't even used anymore; oh wells
-{
-       if (pos >= 0 && pos < MAX_THREADS) {
-               thread_list[pos] = new_thread;
-       } else {
-               ERR("thread position out of bounds");
-       }
-}
-
 #define MAXDATASIZE 1000
-#define POP3 0
-#define IMAP 1
+#define POP3 1
+#define IMAP 2
 
 struct mail_s* parse_mail_args(char type, const char *arg) {
        struct mail_s *mail;
        mail = malloc(sizeof(struct mail_s));
+       memset(mail, 0, sizeof(struct mail_s));
        char *tmp;
        if (sscanf(arg, "%128s %128s %128s", mail->host, mail->user, mail->pass) != 3) {
                if (type == POP3) {
@@ -1105,10 +1267,10 @@ struct mail_s* parse_mail_args(char type, const char *arg) {
        tmp = strstr(arg, "-p ");
        if (tmp) {
                tmp += 3;
-               sscanf(tmp, "%u", &mail->port);
+               sscanf(tmp, "%lu", &mail->port);
        } else {
                if (type == POP3) {
-                       mail->port = 143;       // default pop3 port
+                       mail->port = 110;       // default pop3 port
                } else if (type == IMAP) {
                        mail->port = 143;       // default imap port
                }
@@ -1151,6 +1313,7 @@ void *imap_thread(struct mail_s* mail)
        char *reply;
        int fail = 0;
        unsigned int old_unseen = UINT_MAX;
+       unsigned int old_messages = UINT_MAX;
        struct hostent *he;
        struct sockaddr_in their_addr;  // connector's address information
        if ((he = gethostbyname(mail->host)) == NULL) { // get the host info 
@@ -1158,11 +1321,15 @@ void *imap_thread(struct mail_s* mail)
                exit(1);
        }
        while (threads_runnable == run_code && fail < 5) {
+               if (fail > 0) {
+                       ERR("Trying IMAP connection again for %s@%s (try %i/5)", mail->user, mail->host, fail + 1);
+                       sleep((int)mail->interval);
+               }
                update_time = get_time();
                if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
                        perror("socket");
                        fail++;
-                       break;
+                       continue;
                }
 
                their_addr.sin_family = AF_INET;        // host byte order 
@@ -1175,7 +1342,7 @@ void *imap_thread(struct mail_s* mail)
                     sizeof(struct sockaddr)) == -1) {
                        perror("connect");
                        fail++;
-                       break;
+                       continue;
                }
                struct timeval timeout;
                int res;
@@ -1191,18 +1358,18 @@ void *imap_thread(struct mail_s* mail)
                                  0)) == -1) {
                                perror("recv");
                                fail++;
-                               break;
+                               continue;
                        }
                } else {
-                       ERR("IMAP connection failed: timeout\n");
+                       ERR("IMAP connection failed: timeout");
                        fail++;
-                       break;
+                       continue;
                }
                recvbuf[numbytes] = '\0';
                if (strstr(recvbuf, "* OK") != recvbuf) {
-                       ERR("IMAP connection failed, probably not an IMAP server\n");
+                       ERR("IMAP connection failed, probably not an IMAP server");
                        fail++;
-                       break;
+                       continue;
                }
                strncpy(sendbuf, "a1 login ", MAXDATASIZE);
                strncat(sendbuf, mail->user,
@@ -1214,7 +1381,7 @@ void *imap_thread(struct mail_s* mail)
                if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
                        perror("send a1");
                        fail++;
-                       break;
+                       continue;
                }
                timeout.tv_sec = 60;    // 60 second timeout i guess
                timeout.tv_usec = 0;
@@ -1227,14 +1394,14 @@ void *imap_thread(struct mail_s* mail)
                                  0)) == -1) {
                                perror("recv a1");
                                fail++;
-                               break;
+                               continue;
                        }
                }
                recvbuf[numbytes] = '\0';
                if (strstr(recvbuf, "a1 OK") == NULL) {
-                       ERR("IMAP server login failed: %s\n", recvbuf);
+                       ERR("IMAP server login failed: %s", recvbuf);
                        fail++;
-                       break;
+                       continue;
                }
                strncpy(sendbuf, "a2 STATUS ", MAXDATASIZE);
                strncat(sendbuf, mail->folder,
@@ -1244,7 +1411,7 @@ void *imap_thread(struct mail_s* mail)
                if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
                        perror("send a2");
                        fail++;
-                       break;
+                       continue;
                }
                timeout.tv_sec = 60;    // 60 second timeout i guess
                timeout.tv_usec = 0;
@@ -1257,14 +1424,14 @@ void *imap_thread(struct mail_s* mail)
                                  0)) == -1) {
                                perror("recv a2");
                                fail++;
-                               break;
+                               continue;
                        }
                }
                recvbuf[numbytes] = '\0';
                if (strstr(recvbuf, "a2 OK") == NULL) {
-                       ERR("IMAP status failed: %s\n", recvbuf);
+                       ERR("IMAP status failed: %s", recvbuf);
                        fail++;
-                       break;
+                       continue;
                }
                // now we get the data
                reply = strstr(recvbuf, " (MESSAGES ");
@@ -1273,17 +1440,19 @@ void *imap_thread(struct mail_s* mail)
                if (reply == NULL) {
                        ERR("Error parsing IMAP response: %s", recvbuf);
                        fail++;
-                       break;
+                       continue;
                } else {
-                       sscanf(reply, "MESSAGES %u UNSEEN %u",
+                       pthread_mutex_lock(&(mail->thread_info.mutex));
+                       sscanf(reply, "MESSAGES %lu UNSEEN %lu",
                               &mail->messages,
                               &mail->unseen);
+                       pthread_mutex_unlock(&(mail->thread_info.mutex));
                }
                strncpy(sendbuf, "a3 logout\n", MAXDATASIZE);
                if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
                        perror("send a3");
                        fail++;
-                       break;
+                       continue;
                }
                timeout.tv_sec = 60;    // 60 second timeout i guess
                timeout.tv_usec = 0;
@@ -1296,23 +1465,24 @@ void *imap_thread(struct mail_s* mail)
                                  0)) == -1) {
                                perror("recv a3");
                                fail++;
-                               break;
+                               continue;
                        }
                }
                recvbuf[numbytes] = '\0';
                if (strstr(recvbuf, "a3 OK") == NULL) {
-                       ERR("IMAP logout failed: %s\n", recvbuf);
+                       ERR("IMAP logout failed: %s", recvbuf);
                        fail++;
-                       break;
+                       continue;
                }
                close(sockfd);
-               if (strlen(mail->command) > 1 && mail->unseen > old_unseen) {   // new mail goodie
+               if (strlen(mail->command) > 1 && (mail->unseen > old_unseen || (mail->messages > old_messages && mail->unseen > 0))) {  // new mail goodie
                        if (system(mail->command) == -1) {
                                perror("system()");
                        }
                }
                fail = 0;
                old_unseen = mail->unseen;
+               old_messages = mail->messages;
                mail->last_update = update_time;
                usleep(100);    // prevent race condition
                if (get_time() - mail->last_update >
@@ -1352,11 +1522,15 @@ void *pop3_thread(struct mail_s *mail)
                exit(1);
        }
        while (threads_runnable == run_code && fail < 5) {
+               if (fail > 0) {
+                       ERR("Trying POP3 connection again for %s@%s (try %i/5)", mail->user, mail->host, fail + 1);
+                       sleep((int)mail->interval);
+               }
                update_time = get_time();
                if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
                        perror("socket");
                        fail++;
-                       break;
+                       continue;
                }
 
                their_addr.sin_family = AF_INET;        // host byte order 
@@ -1369,7 +1543,7 @@ void *pop3_thread(struct mail_s *mail)
                     sizeof(struct sockaddr)) == -1) {
                        perror("connect");
                        fail++;
-                       break;
+                       continue;
                }
                struct timeval timeout;
                int res;
@@ -1385,18 +1559,18 @@ void *pop3_thread(struct mail_s *mail)
                                  0)) == -1) {
                                perror("recv");
                                fail++;
-                               break;
+                               continue;
                        }
                } else {
                        ERR("POP3 connection failed: timeout\n");
                        fail++;
-                       break;
+                       continue;
                }
                recvbuf[numbytes] = '\0';
                if (strstr(recvbuf, "+OK ") != recvbuf) {
-                       ERR("POP3 connection failed, probably not a POP3 server\n");
+                       ERR("POP3 connection failed, probably not a POP3 server");
                        fail++;
-                       break;
+                       continue;
                }
                strncpy(sendbuf, "USER ", MAXDATASIZE);
                strncat(sendbuf, mail->user,
@@ -1405,7 +1579,7 @@ void *pop3_thread(struct mail_s *mail)
                if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
                        perror("send USER");
                        fail++;
-                       break;
+                       continue;
                }
                timeout.tv_sec = 60;    // 60 second timeout i guess
                timeout.tv_usec = 0;
@@ -1418,14 +1592,14 @@ void *pop3_thread(struct mail_s *mail)
                                  0)) == -1) {
                                perror("recv USER");
                                fail++;
-                               break;
+                               continue;
                        }
                }
                recvbuf[numbytes] = '\0';
                if (strstr(recvbuf, "+OK ") == NULL) {
-                       ERR("POP3 server login failed: %s\n", recvbuf);
+                       ERR("POP3 server login failed: %s", recvbuf);
                        fail++;
-                       break;
+                       continue;
                }
                strncpy(sendbuf, "PASS ", MAXDATASIZE);
                strncat(sendbuf, mail->pass,
@@ -1434,7 +1608,7 @@ void *pop3_thread(struct mail_s *mail)
                if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
                        perror("send PASS");
                        fail++;
-                       break;
+                       continue;
                }
                timeout.tv_sec = 60;    // 60 second timeout i guess
                timeout.tv_usec = 0;
@@ -1447,20 +1621,20 @@ void *pop3_thread(struct mail_s *mail)
                                  0)) == -1) {
                                perror("recv PASS");
                                fail++;
-                               break;
+                               continue;
                        }
                }
                recvbuf[numbytes] = '\0';
                if (strstr(recvbuf, "+OK ") == NULL) {
-                       ERR("POP3 server login failed: %s\n", recvbuf);
+                       ERR("POP3 server login failed: %s", recvbuf);
                        fail++;
-                       break;
+                       continue;
                }
                strncpy(sendbuf, "STAT\n", MAXDATASIZE);
                if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
                        perror("send STAT");
                        fail++;
-                       break;
+                       continue;
                }
                timeout.tv_sec = 60;    // 60 second timeout i guess
                timeout.tv_usec = 0;
@@ -1473,30 +1647,33 @@ void *pop3_thread(struct mail_s *mail)
                                  0)) == -1) {
                                perror("recv STAT");
                                fail++;
-                               break;
+                               continue;
                        }
                }
                recvbuf[numbytes] = '\0';
                if (strstr(recvbuf, "+OK ") == NULL) {
-                       ERR("POP3 status failed: %s\n", recvbuf);
+                       ERR("POP3 status failed: %s", recvbuf);
                        fail++;
-                       break;
+                       continue;
                }
                // now we get the data
                reply = recvbuf + 4;
                if (reply == NULL) {
                        ERR("Error parsing POP3 response: %s", recvbuf);
                        fail++;
-                       break;
+                       continue;
                } else {
-                       sscanf(reply, "%u %u", &mail->unseen,
+                       pthread_mutex_lock(&(mail->thread_info.mutex));
+                       sscanf(reply, "%lu %lu", &mail->unseen,
                               &mail->used);
+//                     sleep(60);
+                       pthread_mutex_unlock(&(mail->thread_info.mutex));
                }
                strncpy(sendbuf, "QUIT\n", MAXDATASIZE);
                if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
                        perror("send QUIT");
                        fail++;
-                       break;
+                       continue;
                }
                timeout.tv_sec = 60;    // 60 second timeout i guess
                timeout.tv_usec = 0;
@@ -1509,14 +1686,14 @@ void *pop3_thread(struct mail_s *mail)
                                  0)) == -1) {
                                perror("recv QUIT");
                                fail++;
-                               break;
+                               continue;
                        }
                }
                recvbuf[numbytes] = '\0';
                if (strstr(recvbuf, "+OK") == NULL) {
-                       ERR("POP3 logout failed: %s\n", recvbuf);
+                       ERR("POP3 logout failed: %s", recvbuf);
                        fail++;
-                       break;
+                       continue;
                }
                close(sockfd);
                if (strlen(mail->command) > 1 && mail->unseen > old_unseen) {   // new mail goodie
@@ -1555,6 +1732,7 @@ void *threaded_exec(struct text_object *obj) { // pthreads are really beginning
                update_time = get_time();
                char *p2 = obj->data.execi.buffer;
                FILE *fp = popen(obj->data.execi.cmd,"r");
+               pthread_mutex_lock(&(obj->data.execi.thread_info.mutex));
                int n2 = fread(p2, 1, TEXT_BUFFER_SIZE, fp);
                (void) pclose(fp);
                p2[n2] = '\0';
@@ -1567,6 +1745,7 @@ void *threaded_exec(struct text_object *obj) { // pthreads are really beginning
                        }
                        p2++;
                }
+               pthread_mutex_unlock(&(obj->data.execi.thread_info.mutex));
                obj->data.execi.last_update = update_time;
                usleep(100); // prevent race condition
                if (get_time() - obj->data.execi.last_update > obj->data.execi.interval) {
@@ -1619,6 +1798,12 @@ static void free_text_objects(unsigned int count, struct text_object *objs)
                                free(objs[i].data.s);
                                break;
                        case OBJ_utime:
+                               free(objs[i].data.s);
+                               break;
+                       case OBJ_tztime:
+                               free(objs[i].data.tztime.tz);
+                               free(objs[i].data.tztime.fmt);
+                               break;
                        case OBJ_imap:
                                free(info.mail);
                                break;
@@ -1657,6 +1842,9 @@ static void free_text_objects(unsigned int count, struct text_object *objs)
                        case OBJ_text: case OBJ_font:
                                free(objs[i].data.s);
                                break;
+                       case OBJ_image:
+                               free(objs[i].data.s);
+                               break;
                        case OBJ_exec:
                                free(objs[i].data.s);
                                break;
@@ -1672,6 +1860,11 @@ static void free_text_objects(unsigned int count, struct text_object *objs)
                                                case OBJ_execigraph:
                                                free(objs[i].data.s);
                                                break;*/
+#ifdef HAVE_ICONV
+                       case OBJ_iconv_start:
+                               free_iconv();
+                               break;
+#endif
 #ifdef MPD
                        case OBJ_mpd_title:
                                if (info.mpd.title) {
@@ -1852,6 +2045,12 @@ static void free_text_objects(unsigned int count, struct text_object *objs)
                                        info.first_process = NULL;
                                }
                                break;
+#ifdef HDDTEMP
+                       case OBJ_hddtemp:
+                               free(objs[i].data.hddtemp.dev);
+                               free(objs[i].data.hddtemp.addr);
+                               break;
+#endif
                }
        }
        free(objs);
@@ -1875,10 +2074,11 @@ void scan_mixer_bar(const char *arg, int *a, int *w, int *h)
 
 
 /* construct_text_object() creates a new text_object */
-static struct text_object *construct_text_object(const char *s, const char *arg, unsigned int object_count, struct text_object *text_objects)
+static struct text_object *construct_text_object(const char *s, const char *arg, unsigned int object_count, struct text_object *text_objects, long line)
 {
        //struct text_object *obj = new_text_object();
        struct text_object *obj = new_text_object_internal();
+       obj->line = line;
 
 #define OBJ(a, n) if (strcmp(s, #a) == 0) { obj->type = OBJ_##a; need_mask |= (1 << n); {
 #define END ; } } else
@@ -1892,8 +2092,75 @@ static struct text_object *construct_text_object(const char *s, const char *arg,
                OBJ(acpitemp, 0) obj->data.i = open_acpi_temperature(arg);
        END OBJ(acpitempf, 0) obj->data.i = open_acpi_temperature(arg);
        END OBJ(acpiacadapter, 0)
-               END OBJ(freq, 0);
+#if defined(__linux__)
+           END OBJ(freq, 0)
+           get_cpu_count();
+       if (!arg
+           || !isdigit(arg[0])
+           || strlen(arg) >=2
+           || atoi(&arg[0])==0
+           || (unsigned int)atoi(&arg[0])>info.cpu_count)
+       {
+           obj->data.cpu_index=1;
+//         ERR("freq: Invalid CPU number or you don't have that many CPUs! Displaying the clock for CPU 1.");
+       }
+       else 
+       {
+           obj->data.cpu_index=atoi(&arg[0]);
+       }
+       obj->a = 1;
+       END OBJ(freq_g, 0)
+           get_cpu_count();
+       if (!arg
+           || !isdigit(arg[0])
+           || strlen(arg) >=2
+           || atoi(&arg[0])==0
+           || (unsigned int)atoi(&arg[0])>info.cpu_count)
+       {
+           obj->data.cpu_index=1;
+//         ERR("freq_g: Invalid CPU number or you don't have that many CPUs! Displaying the clock for CPU 1.");
+       }
+       else 
+       {
+           obj->data.cpu_index=atoi(&arg[0]);
+       }
+       obj->a = 1;
+       END OBJ(voltage_mv, 0)
+           get_cpu_count();
+       if (!arg
+           || !isdigit(arg[0])
+           || strlen(arg) >=2
+           || atoi(&arg[0])==0
+           || (unsigned int)atoi(&arg[0])>info.cpu_count)
+       {
+           obj->data.cpu_index=1;
+//         ERR("voltage_mv: Invalid CPU number or you don't have that many CPUs! Displaying voltage for CPU 1.");
+       }
+       else 
+       {
+           obj->data.cpu_index=atoi(&arg[0]);
+       }
+       obj->a = 1;
+       END OBJ(voltage_v, 0)
+           get_cpu_count();
+       if (!arg
+           || !isdigit(arg[0])
+           || strlen(arg) >=2
+           || atoi(&arg[0])==0
+           || (unsigned int)atoi(&arg[0])>info.cpu_count)
+       {
+           obj->data.cpu_index=1;
+//         ERR("voltage_v: Invalid CPU number or you don't have that many CPUs! Displaying voltage for CPU 1.");
+       }
+       else 
+       {
+           obj->data.cpu_index=atoi(&arg[0]);
+       }
+       obj->a = 1;
+#else 
+       END OBJ(freq, 0);
        END OBJ(freq_g, 0);
+#endif /* __linux__ */
        END OBJ(freq_dyn, 0);
        END OBJ(freq_dyn_g, 0);
        END OBJ(acpifan, 0);
@@ -2027,8 +2294,10 @@ static struct text_object *construct_text_object(const char *s, const char *arg,
                        ERR("$endif: no matching $if_*");
                }
        END
+       OBJ(image, 0) obj->data.s = strdup(arg ? arg : "");
+       END
 #ifdef HAVE_POPEN
-               OBJ(exec, 0) obj->data.s = strdup(arg ? arg : "");
+       OBJ(exec, 0) obj->data.s = strdup(arg ? arg : "");
        END OBJ(execbar, 0) obj->data.s = strdup(arg ? arg : "");
        END OBJ(execgraph, 0) obj->data.s = strdup(arg ? arg : "");
        END OBJ(execibar, 0) unsigned int n;
@@ -2134,6 +2403,28 @@ static struct text_object *construct_text_object(const char *s, const char *arg,
        END OBJ(hr, 0) obj->data.i = arg ? atoi(arg) : 1;
        END OBJ(offset, 0) obj->data.i = arg ? atoi(arg) : 1;
        END OBJ(voffset, 0) obj->data.i = arg ? atoi(arg) : 1;
+       END OBJ(goto, 0)
+
+       if (!arg) {
+               ERR("goto needs arguments");
+               obj->type = OBJ_text;
+               obj->data.s = strdup("${goto}");
+               return NULL;
+       }
+       
+       obj->data.i = atoi(arg);
+       
+       END OBJ(tab, 0)
+       int a = 10, b = 0;
+       if (arg) {
+               if (sscanf(arg, "%d %d", &a, &b) != 2)
+                       sscanf(arg, "%d", &b);
+       }
+       if (a <= 0) 
+               a = 1;
+       obj->data.pair.a = a;
+       obj->data.pair.b = b;
+
        END OBJ(i2c, INFO_I2C) char buf1[64], buf2[64];
        int n;
 
@@ -2485,6 +2776,48 @@ static struct text_object *construct_text_object(const char *s, const char *arg,
                                obj->data.i2c.devtype);
        END OBJ(time, 0) obj->data.s = strdup(arg ? arg : "%F %T");
        END OBJ(utime, 0) obj->data.s = strdup(arg ? arg : "%F %T");
+       END OBJ(tztime, 0)
+               char buf1[256], buf2[256], *fmt, *tz;
+               fmt = tz = NULL;
+               if (arg) {
+                       int nArgs = sscanf(arg, "%255s %255[^\n]", buf1, buf2);
+                       switch (nArgs) {
+                               case 2:
+                                       tz = buf1;
+                               case 1:
+                                       fmt = buf2;
+                       }
+               }
+
+               obj->data.tztime.fmt = strdup(fmt ? fmt : "%F %T");
+               obj->data.tztime.tz = tz ? strdup(tz) : NULL;
+#ifdef HAVE_ICONV
+       END OBJ(iconv_start, 0)
+               if (iconv_converting) {
+                       CRIT_ERR("You must stop your last iconv conversion before starting another");
+               }
+               if (arg) {
+                       char iconv_from[CODEPAGE_LENGTH];
+                       char iconv_to[CODEPAGE_LENGTH];
+                       if (sscanf(arg, "%s %s", iconv_from, iconv_to) != 2) {
+                               CRIT_ERR("Invalid arguments for iconv_start");
+                       } else {
+                               iconv_t new_iconv;
+                               new_iconv = iconv_open(iconv_to, iconv_from);
+                               if (new_iconv == (iconv_t)(-1)) {
+                                       ERR("Can't convert from %s to %s.", iconv_from, iconv_to);
+                               } else {
+                                       obj->a = register_iconv(&new_iconv);
+                                       iconv_converting = 1;
+                               }
+                       }
+               } else {
+                       CRIT_ERR("Iconv requires arguments");
+               }
+       END OBJ(iconv_stop, 0)
+               iconv_converting = 0;
+       
+#endif
        END OBJ(totaldown, INFO_NET)
                if(arg) {
                        obj->data.net = get_net_stat(arg);
@@ -2655,6 +2988,17 @@ static struct text_object *construct_text_object(const char *s, const char *arg,
                memset(&(info.bmpx), 0, sizeof(struct bmpx_s));
        END
 #endif
+#ifdef HDDTEMP
+       OBJ(hddtemp, 0)
+               if (!arg || scan_hddtemp(arg, &obj->data.hddtemp.dev, 
+                       &obj->data.hddtemp.addr, &obj->data.hddtemp.port)) {
+                       ERR("hddtemp needs arguments");
+                       obj->type = OBJ_text;
+                       obj->data.s = strdup("${hddtemp}");
+                       return NULL;
+               }
+       END
+#endif
 #ifdef TCP_PORT_MONITOR
                OBJ(tcp_portmon, INFO_TCP_PORT_MONITOR) 
                int argc, port_begin, port_end, item, connection_index;
@@ -2709,8 +3053,8 @@ static struct text_object *construct_text_object(const char *s, const char *arg,
                CRIT_ERR("tcp_portmon: connection index must be non-negative");
        }
        /* ok, args looks good. save the text object data */
-       obj->data.tcp_port_monitor.port_range_begin = (in_addr_t)port_begin;
-       obj->data.tcp_port_monitor.port_range_end = (in_addr_t)port_end;
+       obj->data.tcp_port_monitor.port_range_begin = (in_port_t)port_begin;
+       obj->data.tcp_port_monitor.port_range_end = (in_port_t)port_end;
        obj->data.tcp_port_monitor.item = item;
        obj->data.tcp_port_monitor.connection_index = connection_index;
 
@@ -2780,7 +3124,12 @@ static struct text_object_list *extract_variable_text_internal(const char *p)
        memset(retval, 0, sizeof(struct text_object_list));
        retval->text_object_count = 0;
 
+       long line = text_lines;
+
        while (*p) {
+               if (*p == '\n') {
+                       line++;
+               }
                if (*p == '$') {
                        *(char *) p = '\0';
                        obj = create_plain_text(s);
@@ -2852,7 +3201,7 @@ static struct text_object_list *extract_variable_text_internal(const char *p)
                                        }
 
                                        // create new object
-                                       obj = construct_text_object(buf, arg, retval->text_object_count, retval->text_objects);
+                                       obj = construct_text_object(buf, arg, retval->text_object_count, retval->text_objects, line);
                                        if(obj != NULL) {
                                                // allocate memory for the object
                                                retval->text_objects = realloc(retval->text_objects, 
@@ -2909,6 +3258,7 @@ static void extract_variable_text(const char *p)
        ml_cleanup();
 #endif /* MLDONKEY */
 
+
        list = extract_variable_text_internal(p);
        text_objects = list->text_objects;
        text_object_count = list->text_object_count;
@@ -2927,6 +3277,12 @@ void parse_conky_vars(char * text, char * p, struct information *cur) {
 static void generate_text_internal(char *p, int p_max_size, struct text_object *objs, unsigned int object_count, struct information *cur)
 {
        unsigned int i;
+
+#ifdef HAVE_ICONV
+       char buff_in[P_MAX_SIZE] = {0};
+       iconv_converting = 0;
+#endif
+
        for (i = 0; i < object_count; i++) {
                struct text_object *obj = &objs[i];
 
@@ -2965,11 +3321,28 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                                                       i)+ 40) * 9.0 / 5 - 40));
                                }
                                OBJ(freq) {
-                                       get_freq(p, p_max_size, "%.0f", 1); /* pk */
+                                       if (obj->a) {
+                                               obj->a = get_freq(p, p_max_size, "%.0f", 1, obj->data.cpu_index); /* pk */
+                                       }
                                }
                                OBJ(freq_g) {
-                                       get_freq(p, p_max_size, "%'.2f", 1000); /* pk */
+                                       if (obj->a) {
+                                               obj->a = get_freq(p, p_max_size, "%'.2f", 1000, obj->data.cpu_index); /* pk */
+                                       }
+                               }
+#if defined(__linux__)
+                               OBJ(voltage_mv) {
+                                       if (obj->a) {
+                                               obj->a = get_voltage(p, p_max_size, "%.0f", 1, obj->data.cpu_index);
+                                       }
+                               }
+                               OBJ(voltage_v) {
+                                       if (obj->a) {
+                                               obj->a = get_voltage(p, p_max_size, "%'.3f", 1000, obj->data.cpu_index);
+                                       }
                                }
+#endif /* __linux__ */
+
                                OBJ(freq_dyn) {
                                        if (use_spacer) {
                                                get_freq_dynamic(p, 6, "%.0f     ", 1 ); /* pk */
@@ -3123,27 +3496,27 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                OBJ(diskio) {
                                        if (!use_spacer) {
                                                if (diskio_value > 1024*1024) {
-                                                       snprintf(p, p_max_size, "%.1fG",
+                                                       snprintf(p, p_max_size, "%.1fGiB",
                                                                        (double)diskio_value/1024/1024);
                                                } else if (diskio_value > 1024) {
-                                                       snprintf(p, p_max_size, "%.1fM",
+                                                       snprintf(p, p_max_size, "%.1fMiB",
                                                                        (double)diskio_value/1024);
                                                } else if (diskio_value > 0) {
-                                                       snprintf(p, p_max_size, "%dK", diskio_value);
+                                                       snprintf(p, p_max_size, "%dKiB", diskio_value);
                                                } else {
-                                                       snprintf(p, p_max_size, "%d", diskio_value);
+                                                       snprintf(p, p_max_size, "%dB", diskio_value);
                                                }
                                        } else {
                                                if (diskio_value > 1024*1024) {
-                                                       snprintf(p, 6, "%.1fG   ",
+                                                       snprintf(p, 6, "%.1fGiB   ",
                                                                        (double)diskio_value/1024/1024);
                                                } else if (diskio_value > 1024) {
-                                                       snprintf(p, 6, "%.1fM   ",
+                                                       snprintf(p, 6, "%.1fMiB   ",
                                                                        (double)diskio_value/1024);
                                                } else if (diskio_value > 0) {
-                                                       snprintf(p, 6, "%dK ", diskio_value);
+                                                       snprintf(p, 6, "%dKiB ", diskio_value);
                                                } else {
-                                                       snprintf(p, 6, "%d     ", diskio_value);
+                                                       snprintf(p, 6, "%dB     ", diskio_value);
                                                }
                                        }
                                }
@@ -3209,7 +3582,33 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                        snprintf(p, p_max_size, "%d",
                                                        obj->data.net->linkstatus);
                                }
-
+#if defined(IMLIB2) && defined(X11)
+                               OBJ(image) {
+                                       if (obj->a < 1) {
+                                               obj->a++;
+                                       } else {
+                                               Imlib_Image image, buffer;
+                                               image = imlib_load_image(obj->data.s);
+                                               imlib_context_set_image(image);
+                                               if (image) {
+                                                       int w, h;
+                                                       w = imlib_image_get_width();
+                                                       h = imlib_image_get_height();
+                                                       buffer = imlib_create_image(w, h);
+                                                       imlib_context_set_display(display);
+                                                       imlib_context_set_drawable(window.drawable);
+                                                       imlib_context_set_colormap(DefaultColormap(display, screen));
+                                                       imlib_context_set_visual(DefaultVisual(display, screen));
+                                                       imlib_context_set_image(buffer);
+                                                       imlib_blend_image_onto_image(image, 0, 0, 0, w, h, text_start_x, text_start_y, w, h);
+                                                       imlib_render_image_on_drawable(text_start_x, text_start_y);
+                                                       imlib_free_image();
+                                                       imlib_context_set_image(image);
+                                                       imlib_free_image();
+                                               }
+                                       }
+                               }
+#endif /* IMLIB2 */
                                OBJ(exec) {
                                        FILE *fp = popen(obj->data.s, "r");
                                        int length = fread(p, 1, p_max_size, fp);
@@ -3369,11 +3768,13 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                }
                                                obj->data.execi.pos = register_thread(&(obj->data.execi.thread_info));
                                        }
+                                       pthread_mutex_lock(&(obj->data.execi.thread_info.mutex));
                                        snprintf(p, p_max_size, "%s", obj->data.execi.buffer);
+                                       pthread_mutex_unlock(&(obj->data.execi.thread_info.mutex));
                                }
 #endif /* HAVE_POPEN */
                                OBJ(imap_unseen) {
-                                       if (obj->global_mode) { // this means we use info
+                                       if (obj->global_mode && info.mail) { // this means we use info
                                                if (info.mail->pos < 0) {
                                                        info.mail->last_update = current_update_time;
                                                        if (pthread_create(&(info.mail->thread_info.thread), NULL, (void*)imap_thread, (void*) info.mail)) {
@@ -3381,8 +3782,11 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                        }
                                                        info.mail->pos = register_thread(&(info.mail->thread_info));
                                                }
-                                               snprintf(p, p_max_size, "%u", info.mail->unseen);
-                                       } else { // this means we use obj
+                                               // get a lock before reading
+                                               pthread_mutex_lock(&(info.mail->thread_info.mutex));
+                                               snprintf(p, p_max_size, "%lu", info.mail->unseen);
+                                               pthread_mutex_unlock(&(info.mail->thread_info.mutex));
+                                       } else if (obj->data.mail) { // this means we use obj
                                                if (obj->data.mail->pos < 0) {
                                                        obj->data.mail->last_update = current_update_time;
                                                        if (pthread_create(&(obj->data.mail->thread_info.thread), NULL, (void*)imap_thread, (void*) obj->data.mail)) {
@@ -3390,11 +3794,16 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                        }
                                                        obj->data.mail->pos = register_thread(&(obj->data.mail->thread_info));
                                                }
-                                               snprintf(p, p_max_size, "%u", obj->data.mail->unseen);
+                                               pthread_mutex_lock(&(obj->data.mail->thread_info.mutex));
+                                               snprintf(p, p_max_size, "%lu", obj->data.mail->unseen);
+                                               pthread_mutex_unlock(&(obj->data.mail->thread_info.mutex));
+                                       } else if (!obj->a) { // something is wrong, warn once then stop
+                                               ERR("Theres a problem with your imap_unseen settings.  Check that the global IMAP settings are defined properly (line %li).", obj->line);
+                                                       obj->a++;
                                        }
                                }
                                OBJ(imap_messages) {
-                                       if (obj->global_mode) { // this means we use info
+                                       if (obj->global_mode && info.mail) { // this means we use info
                                                if (info.mail->pos < 0) {
                                                        info.mail->last_update = current_update_time;
                                                        if (pthread_create(&(info.mail->thread_info.thread), NULL, (void*)imap_thread, (void*) info.mail)) {
@@ -3402,8 +3811,10 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                        }
                                                        info.mail->pos = register_thread(&(info.mail->thread_info));
                                                }
-                                               snprintf(p, p_max_size, "%u", info.mail->messages);
-                                       } else { // this means we use obj
+                                               pthread_mutex_lock(&(info.mail->thread_info.mutex));
+                                               snprintf(p, p_max_size, "%lu", info.mail->messages);
+                                               pthread_mutex_unlock(&(info.mail->thread_info.mutex));
+                                       } else if (obj->data.mail) { // this means we use obj
                                                if (obj->data.mail->pos < 0) {
                                                        obj->data.mail->last_update = current_update_time;
                                                        if (pthread_create(&(obj->data.mail->thread_info.thread), NULL, (void*)imap_thread, (void*) obj->data.mail)) {
@@ -3411,11 +3822,16 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                        }
                                                        obj->data.mail->pos = register_thread(&(obj->data.mail->thread_info));
                                                }
-                                               snprintf(p, p_max_size, "%u", obj->data.mail->messages);
+                                               pthread_mutex_lock(&(obj->data.mail->thread_info.mutex));
+                                               snprintf(p, p_max_size, "%lu", obj->data.mail->messages);
+                                               pthread_mutex_unlock(&(obj->data.mail->thread_info.mutex));
+                                       } else if (!obj->a) { // something is wrong, warn once then stop
+                                               ERR("Theres a problem with your imap_messages settings.  Check that the global IMAP settings are defined properly (line %li).", obj->line);
+                                                       obj->a++;
                                        }
                                }
                                OBJ(pop3_unseen) {
-                                       if (obj->global_mode) { // this means we use info
+                                       if (obj->global_mode && info.mail) { // this means we use info
                                                if (info.mail->pos < 0) {
                                                        info.mail->last_update = current_update_time;
                                                        if (pthread_create(&(info.mail->thread_info.thread), NULL, (void*)pop3_thread, (void*) info.mail)) {
@@ -3423,8 +3839,10 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                        }
                                                        info.mail->pos = register_thread(&(info.mail->thread_info));
                                                }
-                                               snprintf(p, p_max_size, "%u", info.mail->unseen);
-                                       } else { // this means we use obj
+                                               pthread_mutex_lock(&(info.mail->thread_info.mutex));
+                                               snprintf(p, p_max_size, "%lu", info.mail->unseen);
+                                               pthread_mutex_unlock(&(info.mail->thread_info.mutex));
+                                       } else if (obj->data.mail) { // this means we use obj
                                                if (obj->data.mail->pos < 0) {
                                                        obj->data.mail->last_update = current_update_time;
                                                        if (pthread_create(&(obj->data.mail->thread_info.thread), NULL, (void*)pop3_thread, (void*) obj->data.mail)) {
@@ -3432,11 +3850,16 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                        }
                                                        obj->data.mail->pos = register_thread(&(obj->data.mail->thread_info));
                                                }
-                                               snprintf(p, p_max_size, "%u", obj->data.mail->unseen);
+                                               pthread_mutex_lock(&(obj->data.mail->thread_info.mutex));
+                                               snprintf(p, p_max_size, "%lu", obj->data.mail->unseen);
+                                               pthread_mutex_unlock(&(obj->data.mail->thread_info.mutex));
+                                       } else if (!obj->a) { // something is wrong, warn once then stop
+                                               ERR("Theres a problem with your pop3_unseen settings.  Check that the global POP3 settings are defined properly (line %li).", obj->line);
+                                                       obj->a++;
                                        }
                                }
                                OBJ(pop3_used) {
-                                       if (obj->global_mode) { // this means we use info
+                                       if (obj->global_mode && info.mail) { // this means we use info
                                                if (info.mail->pos < 0) {
                                                        info.mail->last_update = current_update_time;
                                                        if (pthread_create(&(info.mail->thread_info.thread), NULL, (void*)pop3_thread, (void*) info.mail)) {
@@ -3444,8 +3867,10 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                        }
                                                        info.mail->pos = register_thread(&(info.mail->thread_info));
                                                }
+                                               pthread_mutex_lock(&(info.mail->thread_info.mutex));
                                                snprintf(p, p_max_size, "%.1f", info.mail->used/1024.0/1024.0);
-                                       } else { // this means we use obj
+                                               pthread_mutex_unlock(&(info.mail->thread_info.mutex));
+                                       } else if (obj->data.mail) { // this means we use obj
                                                if (obj->data.mail->pos < 0) {
                                                        obj->data.mail->last_update = current_update_time;
                                                        if (pthread_create(&(obj->data.mail->thread_info.thread), NULL, (void*)pop3_thread, (void*) obj->data.mail)) {
@@ -3453,7 +3878,12 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                        }
                                                        obj->data.mail->pos = register_thread(&(obj->data.mail->thread_info));
                                                }
+                                               pthread_mutex_lock(&(obj->data.mail->thread_info.mutex));
                                                snprintf(p, p_max_size, "%.1f", obj->data.mail->used/1024.0/1024.0);
+                                               pthread_mutex_unlock(&(obj->data.mail->thread_info.mutex));
+                                       } else if (!obj->a) { // something is wrong, warn once then stop
+                                               ERR("Theres a problem with your pop3_used settings.  Check that the global POP3 settings are defined properly (line %li).", obj->line);
+                                                       obj->a++;
                                        }
                                }
                        OBJ(fs_bar) {
@@ -3564,9 +3994,29 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                                 v[obj->data.loadavg[0] -
                                                   1]);
                        }
+                       OBJ(goto) {
+                               new_goto(p, obj->data.i);
+                       }
+                       OBJ(tab) {
+                               new_tab(p, obj->data.pair.a, obj->data.pair.b);
+                       }
                        OBJ(hr) {
                                new_hr(p, obj->data.i);
                        }
+                       OBJ(hddtemp) {
+                               char *temp;
+                               char unit;
+                               
+                               temp = get_hddtemp_info(obj->data.hddtemp.dev, 
+                                               obj->data.hddtemp.addr, obj->data.hddtemp.port, &unit);
+                               if (!temp) {
+                                       snprintf(p, p_max_size, "N/A");
+                               } else if (unit == '*') {
+                                       snprintf(p, p_max_size, "%s", temp);
+                               } else {
+                                        snprintf(p, p_max_size, "%s°%c", temp, unit);
+                               }
+                       }
                        OBJ(offset) {
                                new_offset(p, obj->data.i);
                        }
@@ -3630,7 +4080,7 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
 
                        /* memory stuff */
                        OBJ(mem) {
-                               human_readable(cur->mem * 1024, p, 6);
+                               human_readable(cur->mem * 1024, p, 255);
                        }
                        OBJ(memmax) {
                                human_readable(cur->memmax * 1024, p, 255);
@@ -3825,6 +4275,25 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                                struct tm *tm = gmtime(&t);
                                strftime(p, p_max_size, obj->data.s, tm);
                        }
+                       OBJ(tztime) {
+                               char* oldTZ = NULL;
+                               if (obj->data.tztime.tz) {
+                                       oldTZ = getenv("TZ");
+                                       setenv("TZ", obj->data.tztime.tz, 1);
+                                       tzset();
+                               }
+                               time_t t = time(NULL);
+                               struct tm *tm = localtime(&t);
+                               setlocale(LC_TIME, "");
+                               strftime(p, p_max_size, obj->data.tztime.fmt, tm);
+                               if (oldTZ) {
+                                       setenv("TZ", oldTZ, 1);
+                                       tzset();
+                               } else {
+                                       unsetenv("TZ");
+                               }
+                               // Needless to free oldTZ since getenv gives ptr to static data 
+                       }
                        OBJ(totaldown) {
                                human_readable(obj->data.net->recv, p,
                                               255);
@@ -4348,11 +4817,50 @@ static void generate_text_internal(char *p, int p_max_size, struct text_object *
                        }
 #endif
 
+#ifdef HAVE_ICONV
+                       OBJ(iconv_start)
+                       {
+                               iconv_converting = 1;
+                               iconv_selected = obj->a;
+                               
+                       }
+                       OBJ(iconv_stop)
+                       {
+                               iconv_converting = 0;
+                               iconv_selected = 0;
+                       }
+#endif
+
                        break;
                }
 
                {
                        unsigned int a = strlen(p);
+
+#ifdef HAVE_ICONV
+                       if (a > 0 && iconv_converting && iconv_selected > 0 && (iconv_cd[iconv_selected - 1] != (iconv_t)(-1))) {
+                               int bytes;
+                               size_t dummy1, dummy2;
+                               char *ptr = buff_in;
+                               char *outptr = p;
+
+                               dummy1 = dummy2 = a;
+                               
+                               strncpy(buff_in, p, P_MAX_SIZE);
+
+                               iconv(*iconv_cd[iconv_selected - 1], NULL, NULL, NULL, NULL);
+                               while (dummy1 > 0) {
+                                       bytes = iconv(*iconv_cd[iconv_selected - 1], &ptr, &dummy1, &outptr, &dummy2);
+                                       if (bytes == -1) {
+                                               ERR("Iconv codeset conversion failed");
+                                               break;
+                                       }
+                               }
+
+                               /* It is nessecary when we are converting from multibyte to singlebyte codepage */
+                               a = outptr - p;
+                       }
+#endif
                        p += a;
                        p_max_size -= a;
                }
@@ -4367,6 +4875,7 @@ static void generate_text()
        struct information *cur = &info;
        char *p;
 
+
        special_count = 0;
 
        /* update info */
@@ -4393,6 +4902,7 @@ static void generate_text()
                }
        }
 
+
        last_update_time = current_update_time;
        total_updates++;
        //free(p);
@@ -4419,14 +4929,6 @@ static void set_font()
 }
 }
 
-
-/*
- * text size
- */
-
-static int text_start_x, text_start_y; /* text start position in window */
-static int text_width, text_height;
-
 #endif /* X11 */
 
 static inline int get_string_width(const char *s)
@@ -4474,64 +4976,10 @@ static inline int get_string_width_special(char *s)
 #endif /* X11 */
 }
 
-int fontchange = 0;
-
 #ifdef X11
-static void text_size_updater(char *s)
-{
-       int w = 0;
-       char *p;
-       int h = font_height();
-       /* get string widths and skip specials */
-       p = s;
-       while (*p) {
-               if (*p == SPECIAL_CHAR) {
-                       *p = '\0';
-                       w += get_string_width(s);
-                       *p = SPECIAL_CHAR;
-
-                       if (specials[special_index].type == BAR
-                           || specials[special_index].type == GRAPH) {
-                               w += specials[special_index].width;
-                               if (specials[special_index].height > h) {
-                                       h = specials[special_index].height;
-                                       h += font_ascent();
-                               }
-                       }
-                       
-                       else if (specials[special_index].type == OFFSET) {
-                               w += specials[special_index].arg + get_string_width("a"); /* filthy, but works */
-                       }
-                       else if (specials[special_index].type == VOFFSET) {
-                               h += specials[special_index].arg;
-                       }
-                       else if (specials[special_index].type == FONT) {
-                               fontchange = specials[special_index].font_added;
-                               selected_font = specials[special_index].font_added;
-                               h = font_height();
-                       }
-
-                       
-                       special_index++;
-                       s = p + 1;
-               }
-               p++;
-       }
-               w += get_string_width(s);
-       if (w > text_width)
-               text_width = w;
-       if (text_width > maximum_width && maximum_width)
-               text_width = maximum_width;
+static void text_size_updater(char *s);
 
-       text_height += h;
-       if (fontchange) {
-               selected_font = 0;
-       }
-}
-#endif /* X11 */
-
-
-#ifdef X11
+int last_font_height;
 static void update_text_area()
 {
        int x, y;
@@ -4544,7 +4992,9 @@ static void update_text_area()
                text_width = minimum_width;
                text_height = 0;
                special_index = 0;
+               int first_font_height = last_font_height = font_height();
                for_each_line(text_buffer, text_size_updater);
+               text_height -= first_font_height;
                text_width += 1;
                if (text_height < minimum_height)
                        text_height = minimum_height;
@@ -4619,6 +5069,64 @@ static int cur_x, cur_y; /* current x and y for drawing */
 static int draw_mode;          /* FG, BG or OUTLINE */
 static long current_color;
 
+#ifdef X11
+static void text_size_updater(char *s)
+{
+       int w = 0;
+       char *p;
+       /* get string widths and skip specials */
+       p = s;
+       while (*p) {
+               if (*p == SPECIAL_CHAR) {
+                       *p = '\0';
+                       w += get_string_width(s);
+                       *p = SPECIAL_CHAR;
+
+                       if (specials[special_index].type == BAR
+                           || specials[special_index].type == GRAPH) {
+                               w += specials[special_index].width;
+                               if (specials[special_index].height > last_font_height) {
+                                       last_font_height = specials[special_index].height;
+                                       last_font_height += font_ascent();
+                               }
+                       } else if (specials[special_index].type == OFFSET) {
+                               w += specials[special_index].arg + get_string_width("a"); /* filthy, but works */
+                       } else if (specials[special_index].type == VOFFSET) {
+                               last_font_height += specials[special_index].arg;
+                       } else if (specials[special_index].type == GOTO) {
+                               if (specials[special_index].arg >= 0)
+                                       w += (int)specials[special_index].arg - cur_x;
+                       } else if (specials[special_index].type == TAB) { 
+                               int start = specials[special_index].arg;
+                               int step = specials[special_index].width;
+                               if (!step || step < 0)
+                                       step = 10;
+                               w += step - (cur_x - text_start_x - start) % step;
+                       } else if (specials[special_index].type == FONT) {
+                               selected_font = specials[special_index].font_added;
+                               if (font_height() > last_font_height) {
+                                       last_font_height = font_height();
+                               }
+                       }
+                       
+                       special_index++;
+                       s = p + 1;
+               }
+               p++;
+       }
+       w += get_string_width(s);
+       if (w > text_width) {
+               text_width = w;
+       }
+       if (text_width > maximum_width && maximum_width) {
+               text_width = maximum_width;
+       }
+
+       text_height += last_font_height;
+       last_font_height = font_height();
+}
+#endif /* X11 */
+
 static inline void set_foreground_color(long c)
 {
        current_color = c;
@@ -4912,17 +5420,8 @@ static void draw_line(char *s)
                                            specials[special_index].height;
                                        int bar_usage =
                                            specials[special_index].arg;
-                                       int by;
-
-#ifdef XFT
-                                       if (use_xft) {
-                                               by = cur_y - (font_ascent() + h) / 2 - 1;
-                                       } else 
-#endif
-                                       {
-                                               by = cur_y - (font_ascent()/2) - 1;
-                                       }
-                                       if (h < (font_height())) {
+                                       int by = cur_y - (font_ascent() / 2) - 1;
+                                       if (h < font_height()) {
                                                by -= h / 2 - 1;
                                        }
                                        w = specials[special_index].width;
@@ -4966,17 +5465,9 @@ static void draw_line(char *s)
                                        }
                                        int h =
                                            specials[special_index].height;
-                                       int by;
                                        unsigned long last_colour = current_color;
-#ifdef XFT
-                                       if (use_xft) {
-                                            by = cur_y - (font_ascent() + h) / 2 - 1;
-                                       } else
-#endif
-                                       {
-                                               by = cur_y - (font_ascent()/2) - 1;
-                                       }
-                                       if (h < (font_height())) {
+                                       int by = cur_y - (font_ascent()/2) - 1;
+                                       if (h < font_height()) {
                                                by -= h / 2 - 1;
                                        }
                                        w = specials[special_index].width;
@@ -4999,13 +5490,13 @@ static void draw_line(char *s)
        float gradient_factor = 0;
        float gradient_update = 0;
        unsigned long tmpcolour = current_color;
-       if (specials[special_index].last_colour != specials[special_index].first_colour) {
+       if (specials[special_index].last_colour != 0 || specials[special_index].first_colour != 0) {
                tmpcolour = specials[special_index].last_colour;
                gradient_size = gradient_max(specials[special_index].last_colour, specials[special_index].first_colour);
-               gradient_factor = (float)gradient_size / (w - 3);
+               gradient_factor = (float)gradient_size / (w - 2);
        }
-       for (i = w - 3; i > 0; i--) {
-               if (specials[special_index].last_colour != specials[special_index].first_colour) {
+       for (i = w - 2; i > -1; i--) {
+               if (specials[special_index].last_colour != 0 || specials[special_index].first_colour != 0) {
                        XSetForeground(display, window.gc, tmpcolour);
                        gradient_update += gradient_factor;
                        while (gradient_update > 0) {
@@ -5013,10 +5504,10 @@ static void draw_line(char *s)
                                gradient_update--;
                        }
                }
-               if ((w - 3 - i) / ((float) (w - 3) / (specials[special_index].graph_width)) > j) {
+               if ((w - i) / ((float) (w - 2) / (specials[special_index].graph_width)) > j && j < MAX_GRAPH_DEPTH - 3) {
                        j++;
                }
-                                               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 */
+                                               XDrawLine(display,  window.drawable, window.gc, cur_x + i + 1, by + h, cur_x + i + 1, by + h - specials[special_index].graph[j] * (h - 1) / specials[special_index].graph_scale);       /* this is mugfugly, but it works */
                                        }
                                        if (specials[special_index].
                                            height > cur_y_add
@@ -5039,16 +5530,16 @@ static void draw_line(char *s)
                                break;
                        
                                case FONT:
-                               if (fontchange) {
+                               {
+                                       int old = font_ascent();
                                        cur_y -= font_ascent();
                                        selected_font = specials[special_index].font_added;
-                                       cur_y += font_ascent();
-#ifdef XFT
-                                       if (!use_xft || use_xdbe)
-#endif
-                                       {
-                                               set_font();
+                                       if (cur_y + font_ascent() < cur_y + old) {
+                                               cur_y += old;
+                                       } else {
+                                               cur_y += font_ascent();
                                        }
+                                       set_font();
                                }
                                break;
                        case FG:
@@ -5072,14 +5563,26 @@ static void draw_line(char *s)
                                                             arg);
                                break;
 
-                               case OFFSET:
-                               {
-                                       w += specials[special_index].arg;
-                               }
+                       case OFFSET:
+                               w += specials[special_index].arg;
                                break;
-                               case VOFFSET:
+               
+                       case VOFFSET:
+                               cur_y += specials[special_index].arg;
+                               break;
+
+                       case GOTO:
+                               if (specials[special_index].arg >= 0)
+                                       cur_x = (int)specials[special_index].arg;
+                               break;
+
+                       case TAB:
                                {
-                                       cur_y += specials[special_index].arg;
+                                       int start = specials[special_index].arg;
+                                       int step = specials[special_index].width;
+                                       if (!step || step < 0)
+                                               step = 10;
+                                       w = step - (cur_x - text_start_x - start) % step;
                                }
                                break;
 
@@ -5127,9 +5630,6 @@ static void draw_line(char *s)
        draw_string(s);
 
        cur_y += font_descent();
-       if (fontchange) {
-               selected_font = 0;
-       }
 #endif /* X11 */
 }
 
@@ -5172,6 +5672,7 @@ static void draw_text()
 static void draw_stuff()
 {
 #ifdef X11
+       selected_font = 0;
        if (draw_shades && !draw_outline) {
                text_start_x++;
                text_start_y++;
@@ -5183,6 +5684,7 @@ static void draw_stuff()
        }
 
        if (draw_outline) {
+               selected_font = 0;
                int i, j;
                for (i = -1; i < 2; i++)
                        for (j = -1; j < 2; j++) {
@@ -5258,6 +5760,15 @@ static void main_loop()
 
 #ifdef X11
        Region region = XCreateRegion();
+       int event_base, error_base;
+#ifdef HAVE_XDAMAGE
+       if (!XDamageQueryExtension (display, &event_base, &error_base)) {
+               ERR("Xdamage extension unavailable");
+       }
+       Damage damage = XDamageCreate(display, window.window, XDamageReportNonEmpty);
+       XserverRegion region2 = XFixesCreateRegionFromWindow(display, window.window, 0);
+       XserverRegion part = XFixesCreateRegionFromWindow(display, window.window, 0);
+#endif /* HAVE_XDAMAGE */
 #endif /* X11 */
 
        info.looped = 0;
@@ -5317,7 +5828,7 @@ static void main_loop()
 #endif
 
                        need_to_update = 0;
-
+                       selected_font = 0;
                        update_text_area();
 #ifdef OWN_WINDOW
                        if (own_window) {
@@ -5334,11 +5845,11 @@ static void main_loop()
                                            text_height +
                                            border_margin * 2 + 1;
                                        XResizeWindow(display,
-                                                     window.window,
+                                                     window.drawable,
                                                      window.width,
                                                      window.height);
                        if (own_window) {
-                               set_transparent_background(window.window);
+                               set_transparent_background(window.drawable);
                        }
                                     }
 
@@ -5471,6 +5982,15 @@ static void main_loop()
 #endif
 
                        default:
+#ifdef HAVE_XDAMAGE
+                               if (ev.type == event_base + XDamageNotify) {
+                                       XDamageNotifyEvent  *dev = (XDamageNotifyEvent *) &ev;
+                                       XFixesSetRegion(display, part, &dev->area, 1);
+                                       XFixesUnionRegion(display, region2, region2, part);
+                                       XDamageSubtract(display, damage, region2, None);
+                                       XFixesSetRegion(display, region2, 0, 0);
+                               }
+#endif /* HAVE_XDAMAGE */
                                break;
                        }
                }
@@ -5546,10 +6066,13 @@ static void main_loop()
                g_signal_pending=0;
        
        }
-#ifdef X11
+#if defined(X11) && defined(HAVE_XDAMAGE)
+       XDamageDestroy(display, damage);
+       XFixesDestroyRegion(display, region2);
+       XFixesDestroyRegion(display, part);
        XDestroyRegion(region);
        region = NULL;
-#endif /* X11 */
+#endif /* X11 && HAVE_XDAMAGE */
 }
 
 static void load_config_file(const char *);
@@ -5559,6 +6082,10 @@ void reload_config(void)
 {
        //lock_all_threads();
        threads_runnable++;
+       if (info.cpu_usage) {
+               free(info.cpu_usage);
+               info.cpu_usage = NULL;
+       }
 #if defined(XMMS) || defined(BMP) || defined(AUDACIOUS) || defined(INFOPIPE)
         if (info.xmms.thread) {
                if (destroy_xmms_thread()!=0)
@@ -5602,6 +6129,10 @@ void clean_up(void)
 {
        //lock_all_threads();
        threads_runnable++;
+       if (info.cpu_usage) {
+               free(info.cpu_usage);
+               info.cpu_usage = NULL;
+       }
 #ifdef X11
 #ifdef XDBE
        if (use_xdbe) {
@@ -5942,7 +6473,7 @@ else if (strcasecmp(name, a) == 0 || strcasecmp(name, b) == 0)
                }
                CONF("pop3") {
                        if (value) {
-                               info.mail = parse_mail_args(IMAP, value);
+                               info.mail = parse_mail_args(POP3, value);
                        } else {
                                CONF_ERR;
                        }
@@ -6283,6 +6814,7 @@ else if (strcasecmp(name, a) == 0 || strcasecmp(name, b) == 0)
                                        break;
                        }
                        fclose(fp);
+                       text_lines = line + 1;
                        return;
                }
 #ifdef TCP_PORT_MONITOR
@@ -6330,6 +6862,8 @@ else if (strcasecmp(name, a) == 0 || strcasecmp(name, b) == 0)
 
        fclose(fp);
 #undef CONF_ERR
+
+
 }
 
                                                                                                                                                                                        /* : means that character before that takes an argument */
@@ -6401,10 +6935,7 @@ int main(int argc, char **argv)
                switch (c) {
                case 'v':
                case 'V':
-                       printf
-                           ("Conky " VERSION " compiled " __DATE__ "\n");
-                       return 0;
-
+                       print_version();
                case 'c':
                        /* if current_config is set to a strdup of CONFIG_FILE, free it (even
                         * though free() does the NULL check itself;), then load optarg value */
@@ -6599,6 +7130,7 @@ int main(int argc, char **argv)
 
        generate_text();
 #ifdef X11
+       selected_font = 0;
        update_text_area();     /* to get initial size of the window */
 
        init_window
@@ -6607,6 +7139,7 @@ int main(int argc, char **argv)
                 text_height + border_margin * 2 + 1,
                 set_transparent, background_colour, info.uname_s.nodename, argv, argc);
        
+       selected_font = 0;
        update_text_area();     /* to position text/window on screen */
 #endif /* X11 */