Exit properly when forking.
[monky] / src / conky.c
index d3a0c93..ed77172 100644 (file)
@@ -148,6 +148,8 @@ static volatile int g_signal_pending;
 double update_interval;
 void *global_cpu = NULL;
 
+int argc_copy;
+char** argv_copy;
 
 /* prototypes for internally used functions */
 static void signal_handler(int);
@@ -210,7 +212,10 @@ static void print_version(void)
                   "  * RSS\n"
 #endif /* RSS */
 #ifdef WEATHER
-                  "  * Weather (METAR)\n"
+                  "  * Weather (NOAA)\n"
+#ifdef XOAP
+                  "  * Weather (XOAP)\n"
+#endif /* XOAP */
 #endif /* WEATHER */
 #ifdef HAVE_IWLIB
                   "  * wireless\n"
@@ -259,7 +264,6 @@ static const char *suffixes[] = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "" };
 
 #ifdef X11
 
-static void X11_destroy_window(void);
 static void X11_create_window(void);
 static void X11_initialisation(void);
 
@@ -303,7 +307,7 @@ struct information info;
 char *current_config;
 
 /* set to 1 if you want all text to be in uppercase */
-static unsigned int stuff_in_upper_case;
+static unsigned int stuff_in_uppercase;
 
 /* Run how many times? */
 static unsigned long total_run_times;
@@ -317,6 +321,11 @@ static int cpu_avg_samples, net_avg_samples, diskio_avg_samples;
 char *overwrite_file = NULL; FILE *overwrite_fpointer = NULL;
 char *append_file = NULL; FILE *append_fpointer = NULL;
 
+/* xoap suffix for weather from weather.com */
+#ifdef WEATHER
+static char *xoap = NULL;
+#endif /* WEATHER */
+
 #ifdef X11
 
 static int show_graph_scale;
@@ -444,10 +453,14 @@ int check_contains(char *f, char *s)
 }
 
 #ifdef X11
+
+#define SECRIT_MULTILINE_CHAR '\x02'
+
 static inline int calc_text_width(const char *s, int l)
 {
-       if ((output_methods & TO_X) == 0)
+       if ((output_methods & TO_X) == 0) {
                return 0;
+       }
 #ifdef XFT
        if (use_xft) {
                XGlyphInfo gi;
@@ -473,27 +486,24 @@ static inline int calc_text_width(const char *s, int l)
 
 static char *text_buffer;
 
-#ifdef X11
-static unsigned int special_index;     /* used when drawing */
-#endif /* X11 */
-
 /* quite boring functions */
 
-static inline void for_each_line(char *b, void f(char *))
+static inline void for_each_line(char *b, int f(char *, int))
 {
        char *ps, *pe;
+       int special_index = 0; /* specials index */
 
        for (ps = b, pe = b; *pe; pe++) {
                if (*pe == '\n') {
                        *pe = '\0';
-                       f(ps);
+                       special_index = f(ps, special_index);
                        *pe = '\n';
                        ps = pe + 1;
                }
        }
 
        if (ps < pe) {
-               f(ps);
+               f(ps, special_index);
        }
 }
 
@@ -1231,6 +1241,8 @@ static int parse_top_args(const char *s, const char *arg, struct text_object *ob
        return 1;
 }
 
+long current_text_color;
+
 /* construct_text_object() creates a new text_object */
 static struct text_object *construct_text_object(const char *s,
                const char *arg, long line, char allow_threaded, void **ifblock_opaque, void *free_at_crash)
@@ -1643,28 +1655,39 @@ static struct text_object *construct_text_object(const char *s,
 #ifdef X11
                if (output_methods & TO_X) {
                        obj->data.l = arg ? get_x11_color(arg) : default_fg_color;
+                       current_text_color = obj->data.l;
                }
 #endif /* X11 */
        END OBJ(color0, 0)
                obj->data.l = color0;
+               current_text_color = obj->data.l;
        END OBJ(color1, 0)
                obj->data.l = color1;
+               current_text_color = obj->data.l;
        END OBJ(color2, 0)
                obj->data.l = color2;
+               current_text_color = obj->data.l;
        END OBJ(color3, 0)
                obj->data.l = color3;
+               current_text_color = obj->data.l;
        END OBJ(color4, 0)
                obj->data.l = color4;
+               current_text_color = obj->data.l;
        END OBJ(color5, 0)
                obj->data.l = color5;
+               current_text_color = obj->data.l;
        END OBJ(color6, 0)
                obj->data.l = color6;
+               current_text_color = obj->data.l;
        END OBJ(color7, 0)
                obj->data.l = color7;
+               current_text_color = obj->data.l;
        END OBJ(color8, 0)
                obj->data.l = color8;
+               current_text_color = obj->data.l;
        END OBJ(color9, 0)
                obj->data.l = color9;
+               current_text_color = obj->data.l;
 #ifdef X11
        END OBJ(font, 0)
                obj->data.s = scan_font(arg);
@@ -2819,35 +2842,53 @@ static struct text_object *construct_text_object(const char *s,
        END OBJ_THREAD(weather, 0)
                if (arg) {
                        int argc, interval;
-                       char *icao = (char *) malloc(5 * sizeof(char));
+                       char *locID = (char *) malloc(9 * sizeof(char));
                        char *uri = (char *) malloc(128 * sizeof(char));
                        char *data_type = (char *) malloc(32 * sizeof(char));
                        char *tmp_p;
 
-                       argc = sscanf(arg, "%119s %4s %31s %d", uri, icao, data_type, &interval);
+                       argc = sscanf(arg, "%119s %8s %31s %d", uri, locID, data_type, &interval);
 
-                       //icao MUST BE upper-case
-                       tmp_p = icao;
+                       //locID MUST BE upper-case
+                       tmp_p = locID;
                        while (*tmp_p) {
-                               *tmp_p = toupper(*tmp_p);
-                               tmp_p++;
+                         *tmp_p = toupper(*tmp_p);
+                         tmp_p++;
+                       }
+
+                       //Construct complete uri
+                       if (strstr(uri, "xoap.weather.com")) {
+                         if(xoap != NULL) {
+                           strcat(uri, locID);
+                           strcat(uri, xoap);
+                         } else {
+                           free(uri);
+                           uri = NULL;
+                         }
+                       } else if (strstr(uri, "weather.noaa.gov")) {
+                           strcat(uri, locID);
+                           strcat(uri, ".TXT");
+                       } else  if (!strstr(uri, "localhost") && !strstr(uri, "127.0.0.1")) {
+                             CRIT_ERR(obj, free_at_crash, \
+                                      "could not recognize the weather uri");
                        }
 
-
-                       strcat(uri, icao);
-                       strcat(uri, ".TXT");
                        obj->data.weather.uri = uri;
-
                        obj->data.weather.data_type = data_type;
 
-                       // The data retrieval interval is limited to half an hour
+                       //Limit the data retrieval interval to half hour min
                        if (interval < 30) {
                                interval = 30;
                        }
-                       obj->data.weather.interval = interval * 60; // convert to seconds
-                       free(icao);
+
+                       //Convert to seconds
+                       obj->data.weather.interval = interval * 60;
+                       free(locID);
+
+                       DBGP("weather: fetching %s from %s every %d seconds", \
+                            data_type, uri, obj->data.weather.interval);
                } else {
-                       CRIT_ERR(obj, free_at_crash, "weather needs arguments: <uri> <icao> <data_type> [interval in minutes]");
+                       CRIT_ERR(obj, free_at_crash, "weather needs arguments: <uri> <locID> <data_type> [interval in minutes]");
                }
 #endif
 #ifdef HAVE_LUA
@@ -2940,13 +2981,23 @@ static struct text_object *construct_text_object(const char *s,
                        CRIT_ERR(obj, free_at_crash, "to_bytes needs a argument");
                }
        END OBJ(scroll, 0)
-               int n1, n2;
+               int n1 = 0, n2 = 0;
 
+               obj->data.scroll.resetcolor = current_text_color;
                obj->data.scroll.step = 1;
                if (arg && sscanf(arg, "%u %n", &obj->data.scroll.show, &n1) > 0) {
-                       if (sscanf(arg + n1, "%u %n", &obj->data.scroll.step, &n2) > 0)
+                       sscanf(arg + n1, "%u %n", &obj->data.scroll.step, &n2);
+                       if (*(arg + n1 + n2)) {
                                n1 += n2;
-                       obj->data.scroll.text = strndup(arg + n1, text_buffer_size);
+                       } else {
+                               obj->data.scroll.step = 1;
+                       }
+                       obj->data.scroll.text = malloc(strlen(arg + n1) + obj->data.scroll.show + 1);
+                       for(n2 = 0; (unsigned int) n2 < obj->data.scroll.show; n2++) {
+                               obj->data.scroll.text[n2] = ' ';
+                       }
+                       obj->data.scroll.text[n2] = 0;
+                       strcat(obj->data.scroll.text, arg + n1);
                        obj->data.scroll.start = 0;
                        obj->sub = malloc(sizeof(struct text_object));
                        extract_variable_text_internal(obj->sub,
@@ -3651,6 +3702,22 @@ static inline double get_barnum(char *buf)
        return barnum;
 }
 
+/* substitutes all occurrences of '\n' with SECRIT_MULTILINE_CHAR, which allows
+ * multiline objects like $exec work with $align[rc] and friends
+ */
+void substitute_newlines(char *p, long l)
+{
+       char *s = p;
+       if (l < 0) return;
+       while (p && *p && p < s + l) {
+               if (*p == '\n') {
+                       /* only substitute if it's not the last newline */
+                       *p = SECRIT_MULTILINE_CHAR;
+               }
+               p++;
+       }
+}
+
 static void generate_text_internal(char *p, int p_max_size,
                struct text_object root, struct information *cur)
 {
@@ -3671,7 +3738,7 @@ static void generate_text_internal(char *p, int p_max_size,
        p[0] = 0;
        obj = root.next;
        while (obj && p_max_size > 0) {
-               needed = 0; // reset for top stuff
+               needed = 0; /* reset for top stuff */
 
 /* IFBLOCK jumping algorithm
  *
@@ -4165,14 +4232,13 @@ static void generate_text_internal(char *p, int p_max_size,
                        }
 #if defined(__linux__)
                        OBJ(addrs) {
-                               if(NULL != obj->data.net->addrs && strlen(obj->data.net->addrs) > 2)
-                               {
+                               if (NULL != obj->data.net->addrs && strlen(obj->data.net->addrs) > 2) {
                                        obj->data.net->addrs[strlen(obj->data.net->addrs) - 2] = 0; /* remove ", " from end of string */
                                        strcpy(p, obj->data.net->addrs);
+                               } else {
+                                       strcpy(p, "0.0.0.0");
                                }
-                               else
-                                        strcpy(p, "0.0.0.0");
-           }
+                       }
 #endif /* __linux__ */
 #if defined(IMLIB2) && defined(X11)
                        OBJ(image) {
@@ -4642,7 +4708,11 @@ static void generate_text_internal(char *p, int p_max_size,
 #endif
 #ifdef WEATHER
                        OBJ(weather) {
-                               process_weather_info(p, p_max_size, obj->data.weather.uri, obj->data.weather.data_type, obj->data.weather.interval);
+                               if( obj->data.weather.uri != NULL ) {
+                                       process_weather_info(p, p_max_size, obj->data.weather.uri, obj->data.weather.data_type, obj->data.weather.interval);
+                               } else {
+                                 strncpy(p, "either invalid xoap keys file or compiled without xoap support",  p_max_size);
+                               }
                        }
 #endif
 #ifdef HAVE_LUA
@@ -5714,7 +5784,7 @@ static void generate_text_internal(char *p, int p_max_size,
                        OBJ(to_bytes) {
                                char buf[max_user_text];
                                long long bytes;
-                               char unit[16];  //16 because we can also have long names (like mega-bytes)
+                               char unit[16];  // 16 because we can also have long names (like mega-bytes)
 
                                generate_text_internal(buf, max_user_text, *obj->sub, cur);
                                if(sscanf(buf, "%lli%s", &bytes, unit) == 2 && strlen(unit) < 16){
@@ -5727,42 +5797,69 @@ static void generate_text_internal(char *p, int p_max_size,
                                snprintf(p, p_max_size, "%s", buf);
                        }
                        OBJ(scroll) {
-                               unsigned int j;
-                               char *tmp, buf[max_user_text];
+                               unsigned int j, colorchanges = 0, frontcolorchanges = 0, visibcolorchanges = 0, strend;
+                               char *pwithcolors;
+                               char buf[max_user_text];
                                generate_text_internal(buf, max_user_text,
                                                       *obj->sub, cur);
-
-                               if (strlen(buf) <= obj->data.scroll.show) {
-                                       snprintf(p, p_max_size, "%s", buf);
-                                       break;
-                               }
-#define LINESEPARATOR '|'
-                               //place all the lines behind each other with LINESEPARATOR between them
                                for(j = 0; buf[j] != 0; j++) {
-                                       if(buf[j]=='\n') {
+                                       switch(buf[j]) {
+                                       case '\n':      //place all the lines behind each other with LINESEPARATOR between them
+#define LINESEPARATOR '|'
                                                buf[j]=LINESEPARATOR;
+                                               break;
+                                       case SPECIAL_CHAR:
+                                               colorchanges++;
+                                               break;
                                        }
                                }
-                               //scroll the output obj->data.scroll.start places by copying that many chars from
-                               //the front of the string to tmp, scrolling the rest to the front and placing tmp
-                               //at the back of the string
-                               tmp = calloc(obj->data.scroll.start + 1, sizeof(char));
-                               strncpy(tmp, buf, obj->data.scroll.start); tmp[obj->data.scroll.start] = 0;
-                               for(j = obj->data.scroll.start; buf[j] != 0; j++){
-                                       buf[j - obj->data.scroll.start] = buf[j];
-                               }
-                               strcpy(&buf[j - obj->data.scroll.start], tmp);
-                               free(tmp);
-                               //only show the requested number of chars
-                               if(obj->data.scroll.show < j) {
-                                       buf[obj->data.scroll.show] = 0;
-                               }
-                               //next time, scroll a place more or reset scrolling if we are at the end
+                               //no scrolling necessary if the length of the text to scroll is too short
+                               if (strlen(buf) - colorchanges <= obj->data.scroll.show) {
+                                       snprintf(p, p_max_size, "%s", buf);
+                                       break;
+                               }
+                               //make sure a colorchange at the front is not part of the string we are going to show
+                               while(*(buf + obj->data.scroll.start) == SPECIAL_CHAR) {
+                                       obj->data.scroll.start++;
+                               }
+                               //place all chars that should be visible in p, including colorchanges
+                               for(j=0; j < obj->data.scroll.show + visibcolorchanges; j++) {
+                                       p[j] = *(buf + obj->data.scroll.start + j);
+                                       if(p[j] == SPECIAL_CHAR) {
+                                               visibcolorchanges++;
+                                       }
+                                       //if there is still room fill it with spaces
+                                       if( ! p[j]) break;
+                               }
+                               for(; j < obj->data.scroll.show + visibcolorchanges; j++) {
+                                       p[j] = ' ';
+                               }
+                               p[j] = 0;
+                               //count colorchanges in front of the visible part and place that many colorchanges in front of the visible part
+                               for(j = 0; j < obj->data.scroll.start; j++) {
+                                       if(buf[j] == SPECIAL_CHAR) frontcolorchanges++;
+                               }
+                               pwithcolors=malloc(strlen(p) + 1 + colorchanges - visibcolorchanges);
+                               for(j = 0; j < frontcolorchanges; j++) {
+                                       pwithcolors[j] = SPECIAL_CHAR;
+                               }
+                               pwithcolors[j] = 0;
+                               strcat(pwithcolors,p);
+                               strend = strlen(pwithcolors);
+                               //and place the colorchanges not in front or in the visible part behind the visible part
+                               for(j = 0; j < colorchanges - frontcolorchanges - visibcolorchanges; j++) {
+                                       pwithcolors[strend + j] = SPECIAL_CHAR;
+                               }
+                               pwithcolors[strend + j] = 0;
+                               strcpy(p, pwithcolors);
+                               free(pwithcolors);
+                               //scroll
                                obj->data.scroll.start += obj->data.scroll.step;
-                               if(obj->data.scroll.start >= j){
+                               if(buf[obj->data.scroll.start] == 0){
                                         obj->data.scroll.start = 0;
                                }
-                               snprintf(p, p_max_size, "%s", buf);
+                               //reset color when scroll is finished
+                               new_fg(p + strlen(p), obj->data.scroll.resetcolor);
                        }
                        OBJ(combine) {
                                char buf[2][max_user_text];
@@ -5817,12 +5914,12 @@ static void generate_text_internal(char *p, int p_max_size,
                                        }
                                        strcat(p, "\n");
                                        #ifdef HAVE_OPENMP
-                                       #pragma omp parallel for
+                                       #pragma omp parallel for schedule(dynamic,10)
                                        #endif /* HAVE_OPENMP */
                                        for(i=0; i<2; i++) if(current[i]) current[i]=current[i]->next;
                                }
                                #ifdef HAVE_OPENMP
-                               #pragma omp parallel for
+                               #pragma omp parallel for schedule(dynamic,10)
                                #endif /* HAVE_OPENMP */
                                for(i=0; i<2; i++) {
                                        while(ll_rows[i] != NULL) {
@@ -5964,6 +6061,9 @@ static void generate_text_internal(char *p, int p_max_size,
                                a = outptr - p;
                        }
 #endif /* HAVE_ICONV */
+                       if (obj->type != OBJ_text) {
+                               substitute_newlines(p, a - 2);
+                       }
                        p += a;
                        p_max_size -= a;
                }
@@ -6014,7 +6114,7 @@ static void generate_text(void)
 
        generate_text_internal(p, max_user_text, global_root_object, cur);
 
-       if (stuff_in_upper_case) {
+       if (stuff_in_uppercase) {
                char *tmp_p;
 
                tmp_p = text_buffer;
@@ -6032,7 +6132,6 @@ static void generate_text(void)
        }
        last_update_time = current_update_time;
        total_updates++;
-       // free(p);
 }
 
 static inline int get_string_width(const char *s)
@@ -6045,7 +6144,7 @@ static inline int get_string_width(const char *s)
        return strlen(s);
 }
 
-static inline int get_string_width_special(char *s)
+static int get_string_width_special(char *s, int special_index)
 {
 #ifdef X11
        char *p, *final;
@@ -6054,7 +6153,7 @@ static inline int get_string_width_special(char *s)
        long i;
 
        if ((output_methods & TO_X) == 0) {
-#endif
+#endif /* X11 */
                return (s) ? strlen(s) : 0;
 #ifdef X11
        }
@@ -6079,6 +6178,9 @@ static inline int get_string_width_special(char *s)
                                width += specials[special_index + idx].width;
                        }
                        idx++;
+               } else if (*p == SECRIT_MULTILINE_CHAR) {
+                       *p = 0;
+                       break;
                } else {
                        p++;
                }
@@ -6092,7 +6194,7 @@ static inline int get_string_width_special(char *s)
 }
 
 #ifdef X11
-static void text_size_updater(char *s);
+static int text_size_updater(char *s, int special_index);
 
 int last_font_height;
 static void update_text_area(void)
@@ -6108,7 +6210,6 @@ static void update_text_area(void)
        {
                text_width = minimum_width;
                text_height = 0;
-               special_index = 0;
                last_font_height = font_height();
                for_each_line(text_buffer, text_size_updater);
                text_width += 1;
@@ -6211,13 +6312,13 @@ static int draw_mode;           /* FG, BG or OUTLINE */
 #ifdef X11
 static long current_color;
 
-static void text_size_updater(char *s)
+static int text_size_updater(char *s, int special_index)
 {
        int w = 0;
        char *p;
 
        if ((output_methods & TO_X) == 0)
-               return;
+               return 0;
        /* get string widths and skip specials */
        p = s;
        while (*p) {
@@ -6261,6 +6362,14 @@ static void text_size_updater(char *s)
 
                        special_index++;
                        s = p + 1;
+               } else if (*p == SECRIT_MULTILINE_CHAR) {
+                       int lw;
+                       *p = '\0';
+                       lw = get_string_width(s);
+                       *p = SECRIT_MULTILINE_CHAR;
+                       s = p + 1;
+                       w = lw > w ? lw : w;
+                       text_height += last_font_height;
                }
                p++;
        }
@@ -6274,6 +6383,7 @@ static void text_size_updater(char *s)
 
        text_height += last_font_height;
        last_font_height = font_height();
+       return special_index;
 }
 
 static inline void set_foreground_color(long c)
@@ -6386,36 +6496,39 @@ static void draw_string(const char *s)
        memcpy(tmpstring1, s, text_buffer_size);
 }
 
-static void draw_line(char *s)
-{
 #ifdef X11
-       char *p;
+int draw_each_line_inner(char *s, int special_index, int last_special_applied)
+{
+       int font_h = font_height();
        int cur_y_add = 0;
-       int font_h;
-       char *tmp_str;
+       char *recurse = 0;
+       char *p = s;
+       int last_special_needed = -1;
+       int orig_special_index = special_index;
 
-       if ((output_methods & TO_X) == 0) {
-#endif /* X11 */
-               draw_string(s);
-               return;
-#ifdef X11
-       }
        cur_x = text_start_x;
        cur_y += font_ascent();
-       font_h = font_height();
 
-       /* find specials and draw stuff */
-       p = s;
        while (*p) {
-               if (*p == SPECIAL_CHAR) {
-                       int w = 0;
-
-                       /* draw string before special */
+               if (*p == SECRIT_MULTILINE_CHAR) {
+                       /* special newline marker for multiline objects */
+                       recurse = p + 1;
                        *p = '\0';
-                       draw_string(s);
-                       *p = SPECIAL_CHAR;
-                       s = p + 1;
+                       break;
+               }
+               if (*p == SPECIAL_CHAR || last_special_applied > -1) {
+                       int w = 0;
 
+                       /* draw string before special, unless we're dealing multiline
+                        * specials */
+                       if (last_special_applied > -1) {
+                               special_index = last_special_applied;
+                       } else {
+                               *p = '\0';
+                               draw_string(s);
+                               *p = SPECIAL_CHAR;
+                               s = p + 1;
+                       }
                        /* draw special */
                        switch (specials[special_index].type) {
                                case HORIZONTAL_LINE:
@@ -6638,6 +6751,7 @@ static void draw_line(char *s)
                                                char *tmp_hour_str;
                                                char *tmp_min_str;
                                                char *tmp_sec_str;
+                                               char *tmp_str;
                                                unsigned short int timeunits;
                                                if (seconds != 0) {
                                                        timeunits = seconds / 86400; seconds %= 86400;
@@ -6679,6 +6793,7 @@ static void draw_line(char *s)
                                        if (show_graph_scale && (specials[special_index].show_scale == 1)) {
                                                int tmp_x = cur_x;
                                                int tmp_y = cur_y;
+                                               char *tmp_str;
                                                cur_x += font_ascent() / 2;
                                                cur_y += font_h / 2;
                                                tmp_str = (char *)
@@ -6730,6 +6845,7 @@ static void draw_line(char *s)
 
                                case OFFSET:
                                        w += specials[special_index].arg;
+                                       last_special_needed = special_index;
                                        break;
 
                                case VOFFSET:
@@ -6740,6 +6856,7 @@ static void draw_line(char *s)
                                        if (specials[special_index].arg >= 0) {
                                                cur_x = (int) specials[special_index].arg;
                                        }
+                                       last_special_needed = special_index;
                                        break;
 
                                case TAB:
@@ -6751,6 +6868,7 @@ static void draw_line(char *s)
                                                step = 10;
                                        }
                                        w = step - (cur_x - text_start_x - start) % step;
+                                       last_special_needed = special_index;
                                        break;
                                }
 
@@ -6759,7 +6877,7 @@ static void draw_line(char *s)
                                        /* TODO: add back in "+ window.border_inner_margin" to the end of
                                         * this line? */
                                        int pos_x = text_start_x + text_width -
-                                               get_string_width_special(s);
+                                               get_string_width_special(s, special_index);
 
                                        /* printf("pos_x %i text_start_x %i text_width %i cur_x %i "
                                                "get_string_width(p) %i gap_x %i "
@@ -6771,13 +6889,15 @@ static void draw_line(char *s)
                                        if (pos_x > specials[special_index].arg && pos_x > cur_x) {
                                                cur_x = pos_x - specials[special_index].arg;
                                        }
+                                       last_special_needed = special_index;
                                        break;
                                }
 
                                case ALIGNC:
                                {
-                                       int pos_x = (text_width) / 2 - get_string_width_special(s) /
-                                               2 - (cur_x - text_start_x);
+                                       int pos_x = (text_width) / 2 - get_string_width_special(s,
+                                                       special_index) / 2 - (cur_x -
+                                                               text_start_x);
                                        /* int pos_x = text_start_x + text_width / 2 -
                                                get_string_width_special(s) / 2; */
 
@@ -6789,24 +6909,46 @@ static void draw_line(char *s)
                                        if (pos_x > specials[special_index].arg) {
                                                w = pos_x - specials[special_index].arg;
                                        }
+                                       last_special_needed = special_index;
                                        break;
                                }
                        }
 
                        cur_x += w;
 
-                       special_index++;
+                       if (special_index != last_special_applied) {
+                               special_index++;
+                       } else {
+                               special_index = orig_special_index;
+                               last_special_applied = -1;
+                       }
                }
-
                p++;
        }
 
-       if (cur_y_add > 0) {
-               cur_y += cur_y_add;
-       }
+       cur_y += cur_y_add;
        draw_string(s);
        cur_y += font_descent();
+       if (recurse && *recurse) {
+               special_index = draw_each_line_inner(recurse, special_index, last_special_needed);
+               *(recurse - 1) = SECRIT_MULTILINE_CHAR;
+       }
+       return special_index;
+}
+#endif /* X11 */
+
+static int draw_line(char *s, int special_index)
+{
+#ifdef X11
+       if ((output_methods & TO_X) == 0) {
+#endif /* X11 */
+               draw_string(s);
+               return 0;
+#ifdef X11
+       }
 
+       /* find specials and draw stuff */
+       return draw_each_line_inner(s, special_index, -1);
 #endif /* X11 */
 }
 
@@ -6839,7 +6981,6 @@ static void draw_text(void)
                }
 
                /* draw text */
-               special_index = 0;
        }
        setup_fonts();
 #endif /* X11 */
@@ -7407,83 +7548,18 @@ static void main_loop(void)
 }
 
 static void load_config_file(const char *);
+#ifdef X11
 static void load_config_file_x11(const char *);
+#endif /* X11 */
+void initialisation(int argc, char** argv);
 
        /* reload the config file */
 static void reload_config(void)
 {
-       timed_thread_destroy_registered_threads();
-
-       if (info.cpu_usage) {
-               free(info.cpu_usage);
-               info.cpu_usage = NULL;
-       }
-
-       if (info.mail) {
-               free(info.mail);
-       }
-
-#ifdef X11
-       free_fonts();
-#endif /* X11 */
-
-#ifdef TCP_PORT_MONITOR
-       tcp_portmon_clear();
-#endif
-
-#ifdef RSS
-       free_rss_info();
-#endif
-#ifdef WEATHER
-       free_weather_info();
-#endif
-#ifdef HAVE_LUA
-       llua_close();
-#endif /* HAVE_LUA */
-
-#ifdef X11
-       X11_destroy_window();
-#endif /* X11 */
-
-       if (current_config) {
-               clear_fs_stats();
-               load_config_file(current_config);
-               load_config_file_x11(current_config);
-
-               /* re-init specials array */
-               if ((specials = realloc((void *) specials,
-                               sizeof(struct special_t) * max_specials)) == 0) {
-                       ERR("failed to realloc specials array");
-               }
-
-#ifdef X11
-               if (output_methods & TO_X) {
-                       X11_initialisation();
-               }
-#endif /* X11 */
-               extract_variable_text(global_text);
-               free(global_text);
-               global_text = NULL;
-               if (tmpstring1) {
-                       free(tmpstring1);
-               }
-               tmpstring1 = malloc(text_buffer_size);
-               memset(tmpstring1, 0, text_buffer_size);
-               if (tmpstring2) {
-                       free(tmpstring2);
-               }
-               tmpstring2 = malloc(text_buffer_size);
-               memset(tmpstring2, 0, text_buffer_size);
-               if (text_buffer) {
-                       free(text_buffer);
-               }
-               text_buffer = malloc(max_user_text);
-               memset(text_buffer, 0, max_user_text);
-#ifdef X11
-               X11_create_window();
-#endif /* X11 */
-               update_text();
-       }
+       char *current_config_copy = strdup(current_config);
+       clean_up(NULL, NULL);
+       current_config = current_config_copy;
+       initialisation(argc_copy, argv_copy);
 }
 
 void clean_up(void *memtofree1, void* memtofree2)
@@ -7511,6 +7587,7 @@ void clean_up(void *memtofree1, void* memtofree2)
                }
                XClearWindow(display, RootWindow(display, screen));
                XCloseDisplay(display);
+               display = NULL;
                if(info.x11.desktop.all_names) {
                        free(info.x11.desktop.all_names);
                        info.x11.desktop.all_names = NULL;
@@ -7519,8 +7596,10 @@ void clean_up(void *memtofree1, void* memtofree2)
                        free(info.x11.desktop.name);
                        info.x11.desktop.name = NULL;
                }
+               x_initialised = NO;
        }else{
                free(fonts);    //in set_default_configurations a font is set but not loaded
+               font_count = -1;
        }
 
 #endif /* X11 */
@@ -7579,7 +7658,10 @@ void clean_up(void *memtofree1, void* memtofree2)
 
        clear_net_stats();
        clear_diskio_stats();
-       if(global_cpu != NULL) free(global_cpu);
+       if(global_cpu != NULL) {
+               free(global_cpu);
+               global_cpu = NULL;
+       }
 }
 
 static int string_to_bool(const char *s)
@@ -7655,6 +7737,7 @@ static void set_default_configurations_for_x(void)
        color7 = default_fg_color;
        color8 = default_fg_color;
        color9 = default_fg_color;
+       current_text_color = default_fg_color;
 }
 #endif /* X11 */
 
@@ -7782,7 +7865,7 @@ static void set_default_configurations(void)
        no_buffers = 1;
        update_interval = 3.0;
        info.music_player_interval = 1.0;
-       stuff_in_upper_case = 0;
+       stuff_in_uppercase = 0;
        info.users.number = 1;
 
 #ifdef TCP_PORT_MONITOR
@@ -7859,24 +7942,6 @@ static void X11_initialisation(void)
 #endif /* DEBUG */
 }
 
-static void X11_destroy_window(void)
-{
-       /* this function only exists for the sake of consistency */
-       if (output_methods & TO_X) {
-#ifdef HAVE_XDAMAGE
-               XDamageDestroy(display, x11_stuff.damage);
-               XFixesDestroyRegion(display, x11_stuff.region2);
-               XFixesDestroyRegion(display, x11_stuff.part);
-               if (x11_stuff.region) {
-                       XDestroyRegion(x11_stuff.region);
-               }
-               x11_stuff.region = NULL;
-#endif /* HAVE_XDAMAGE */
-               destroy_window();
-       }
-       x_initialised = NO;
-}
-
 static char **xargv = 0;
 static int xargc = 0;
 
@@ -8025,7 +8090,7 @@ static void load_config_file(const char *f)
                CONF2("out_to_x") {
                        /* don't listen if X is already initialised or
                         * if we already know we don't want it */
-                       if(x_initialised == NO) {
+                       if(x_initialised != YES) {
                                if (string_to_bool(value)) {
                                        output_methods &= TO_X;
                                } else {
@@ -8572,7 +8637,7 @@ static void load_config_file(const char *f)
                        }
                }
                CONF("uppercase") {
-                       stuff_in_upper_case = string_to_bool(value);
+                       stuff_in_uppercase = string_to_bool(value);
                }
                CONF("max_specials") {
                        if (value) {
@@ -8772,6 +8837,7 @@ static void load_config_file(const char *f)
        }
 }
 
+#ifdef X11
 static void load_config_file_x11(const char *f)
 {
        int line = 0;
@@ -8792,7 +8858,6 @@ static void load_config_file_x11(const char *f)
                        continue;
                }
 
-#ifdef X11
                CONF2("color0") {
                        X11_initialisation();
                        if (x_initialised == YES) {
@@ -8937,12 +9002,11 @@ static void load_config_file_x11(const char *f)
                }
 #endif
                CONF("text") {
-                       //initialize X11 if nothing X11-related is mentioned before TEXT (and if X11 is the default outputmethod)
+                       /* initialize X11 if nothing X11-related is mentioned before TEXT (and if X11 is the default outputmethod) */
                        if(output_methods & TO_X) {
                                X11_initialisation();
                        }
                }
-#endif /* X11 */
 #undef CONF
 #undef CONF2
 #undef CONF3
@@ -8956,6 +9020,43 @@ static void load_config_file_x11(const char *f)
        fclose(fp);
 
 }
+#endif /* X11 */
+
+#if defined(WEATHER) && defined(XOAP)
+/*
+ * TODO: make the xoap keys file readable from the config file
+ *       make the keys directly readable from the config file
+ *       make the xoap keys file giveable as a command line option
+ */
+static void load_xoap_keys(void)
+{
+  FILE *fp;
+  char *par = (char *) malloc(11 * sizeof(char));
+  char *key = (char *) malloc(17 * sizeof(char));
+
+  xoap = (char *) malloc(64 * sizeof(char));
+  to_real_path(xoap, XOAP_FILE);
+  fp = fopen(xoap, "r");
+  if (fp != NULL) {
+    if( fscanf(fp, "%10s %16s", par, key) == 2 ) {
+      strcpy(xoap, "?cc=*&link=xoap&prod=xoap&par=");
+      strcat(xoap, par);
+      strcat(xoap, "&key=");
+      strcat(xoap, key);
+      strcat(xoap, "&unit=m");
+    } else {
+      free(xoap);
+      xoap = NULL;
+    }
+    fclose(fp);
+  } else {
+    free(xoap);
+    xoap = NULL;
+  }
+  free(par);
+  free(key);
+}
+#endif /* WEATHER && XOAP */
 
 static void print_help(const char *prog_name) {
        printf("Usage: %s [OPTION]...\n"
@@ -9035,133 +9136,9 @@ static const struct option longopts[] = {
        { 0, 0, 0, 0 }
 };
 
-int main(int argc, char **argv)
-{
-#ifdef X11
-       char *s, *temp;
-       unsigned int x;
-#endif
+void initialisation(int argc, char **argv) {
        struct sigaction act, oact;
 
-       g_signal_pending = 0;
-       max_user_text = MAX_USER_TEXT_DEFAULT;
-       current_config = 0;
-       memset(&info, 0, sizeof(info));
-       memset(template, 0, sizeof(template));
-       clear_net_stats();
-
-#ifdef TCP_PORT_MONITOR
-       /* set default connection limit */
-       tcp_portmon_set_max_connections(0);
-#endif
-
-       /* handle command line parameters that don't change configs */
-#ifdef X11
-       if (((s = getenv("LC_ALL")) && *s) || ((s = getenv("LC_CTYPE")) && *s)
-                       || ((s = getenv("LANG")) && *s)) {
-               temp = (char *) malloc((strlen(s) + 1) * sizeof(char));
-               if (temp == NULL) {
-                       ERR("malloc failed");
-               }
-               for (x = 0; x < strlen(s); x++) {
-                       temp[x] = tolower(s[x]);
-               }
-               temp[x] = 0;
-               if (strstr(temp, "utf-8") || strstr(temp, "utf8")) {
-                       utf8_mode = 1;
-               }
-
-               free(temp);
-       }
-       if (!setlocale(LC_CTYPE, "")) {
-               ERR("Can't set the specified locale!\nCheck LANG, LC_CTYPE, LC_ALL.");
-       }
-#endif /* X11 */
-       while (1) {
-               int c = getopt_long(argc, argv, getopt_string, longopts, NULL);
-
-               if (c == -1) {
-                       break;
-               }
-
-               switch (c) {
-                       case 'v':
-                       case 'V':
-                               print_version();
-                       case 'c':
-                               if (current_config) {
-                                       free(current_config);
-                               }
-                               current_config = strndup(optarg, max_user_text);
-                               break;
-                       case 'q':
-                               freopen("/dev/null", "w", stderr);
-                               break;
-                       case 'h':
-                               print_help(argv[0]);
-                               return 0;
-#ifdef CONFIG_OUTPUT
-                       case 'C':
-                               print_defconfig();
-                               return 0;
-#endif
-#ifdef X11
-                       case 'w':
-                               window.window = strtol(optarg, 0, 0);
-                               break;
-#endif /* X11 */
-
-                       case '?':
-                               exit(EXIT_FAILURE);
-               }
-       }
-
-       /* check if specified config file is valid */
-       if (current_config) {
-               struct stat sb;
-               if (stat(current_config, &sb) ||
-                               (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))) {
-                       ERR("invalid configuration file '%s'\n", current_config);
-                       free(current_config);
-                       current_config = 0;
-               }
-       }
-
-       /* load current_config, CONFIG_FILE or SYSTEM_CONFIG_FILE */
-
-       if (!current_config) {
-               /* load default config file */
-               char buf[DEFAULT_TEXT_BUFFER_SIZE];
-               FILE *fp;
-
-               /* Try to use personal config file first */
-               to_real_path(buf, CONFIG_FILE);
-               if (buf[0] && (fp = fopen(buf, "r"))) {
-                       current_config = strndup(buf, max_user_text);
-                       fclose(fp);
-               }
-
-               /* Try to use system config file if personal config not readable */
-               if (!current_config && (fp = fopen(SYSTEM_CONFIG_FILE, "r"))) {
-                       current_config = strndup(SYSTEM_CONFIG_FILE, max_user_text);
-                       fclose(fp);
-               }
-
-               /* No readable config found */
-               if (!current_config) {
-#ifdef CONFIG_OUTPUT
-                       current_config = strdup("==builtin==");
-                       ERR("no readable personal or system-wide config file found,"
-                                       " using builtin default");
-#else
-                       CRIT_ERR(NULL, NULL, "no readable personal or system-wide config file found");
-#endif /* ! CONF_OUTPUT */
-               }
-       }
-#ifdef HAVE_SYS_INOTIFY_H
-       inotify_fd = inotify_init();
-#endif /* HAVE_SYS_INOTIFY_H */
-
        load_config_file(current_config);
 
        /* init specials array */
@@ -9306,7 +9283,7 @@ int main(int argc, char **argv)
                                fprintf(stderr, PACKAGE_NAME": forked to background, pid is %d\n",
                                        pid);
                                fflush(stderr);
-                               return 0;
+                               exit(EXIT_SUCCESS);
                }
        }
 
@@ -9338,6 +9315,144 @@ int main(int argc, char **argv)
                ERR("error setting signal handler: %s", strerror(errno));
        }
 
+}
+
+int main(int argc, char **argv)
+{
+#ifdef X11
+       char *s, *temp;
+       unsigned int x;
+#endif
+
+       argc_copy = argc;
+       argv_copy = argv;
+       g_signal_pending = 0;
+       max_user_text = MAX_USER_TEXT_DEFAULT;
+       current_config = 0;
+       memset(&info, 0, sizeof(info));
+       memset(template, 0, sizeof(template));
+       clear_net_stats();
+
+#ifdef TCP_PORT_MONITOR
+       /* set default connection limit */
+       tcp_portmon_set_max_connections(0);
+#endif
+
+       /* handle command line parameters that don't change configs */
+#ifdef X11
+       if (((s = getenv("LC_ALL")) && *s) || ((s = getenv("LC_CTYPE")) && *s)
+                       || ((s = getenv("LANG")) && *s)) {
+               temp = (char *) malloc((strlen(s) + 1) * sizeof(char));
+               if (temp == NULL) {
+                       ERR("malloc failed");
+               }
+               for (x = 0; x < strlen(s); x++) {
+                       temp[x] = tolower(s[x]);
+               }
+               temp[x] = 0;
+               if (strstr(temp, "utf-8") || strstr(temp, "utf8")) {
+                       utf8_mode = 1;
+               }
+
+               free(temp);
+       }
+       if (!setlocale(LC_CTYPE, "")) {
+               ERR("Can't set the specified locale!\nCheck LANG, LC_CTYPE, LC_ALL.");
+       }
+#endif /* X11 */
+       while (1) {
+               int c = getopt_long(argc, argv, getopt_string, longopts, NULL);
+
+               if (c == -1) {
+                       break;
+               }
+
+               switch (c) {
+                       case 'v':
+                       case 'V':
+                               print_version();
+                       case 'c':
+                               if (current_config) {
+                                       free(current_config);
+                               }
+                               current_config = strndup(optarg, max_user_text);
+                               break;
+                       case 'q':
+                               freopen("/dev/null", "w", stderr);
+                               break;
+                       case 'h':
+                               print_help(argv[0]);
+                               return 0;
+#ifdef CONFIG_OUTPUT
+                       case 'C':
+                               print_defconfig();
+                               return 0;
+#endif
+#ifdef X11
+                       case 'w':
+                               window.window = strtol(optarg, 0, 0);
+                               break;
+#endif /* X11 */
+
+                       case '?':
+                               exit(EXIT_FAILURE);
+               }
+       }
+
+       /* check if specified config file is valid */
+       if (current_config) {
+               struct stat sb;
+               if (stat(current_config, &sb) ||
+                               (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))) {
+                       ERR("invalid configuration file '%s'\n", current_config);
+                       free(current_config);
+                       current_config = 0;
+               }
+       }
+
+       /* load current_config, CONFIG_FILE or SYSTEM_CONFIG_FILE */
+
+       if (!current_config) {
+               /* load default config file */
+               char buf[DEFAULT_TEXT_BUFFER_SIZE];
+               FILE *fp;
+
+               /* Try to use personal config file first */
+               to_real_path(buf, CONFIG_FILE);
+               if (buf[0] && (fp = fopen(buf, "r"))) {
+                       current_config = strndup(buf, max_user_text);
+                       fclose(fp);
+               }
+
+               /* Try to use system config file if personal config not readable */
+               if (!current_config && (fp = fopen(SYSTEM_CONFIG_FILE, "r"))) {
+                       current_config = strndup(SYSTEM_CONFIG_FILE, max_user_text);
+                       fclose(fp);
+               }
+
+               /* No readable config found */
+               if (!current_config) {
+#ifdef CONFIG_OUTPUT
+                       current_config = strdup("==builtin==");
+                       ERR("no readable personal or system-wide config file found,"
+                                       " using builtin default");
+#else
+                       CRIT_ERR(NULL, NULL, "no readable personal or system-wide config file found");
+#endif /* ! CONF_OUTPUT */
+               }
+       }
+
+#if defined(WEATHER) && defined(XOAP)
+       /* Load xoap keys, if existing */
+       load_xoap_keys();
+#endif /* WEATHER && XOAP */
+
+#ifdef HAVE_SYS_INOTIFY_H
+       inotify_fd = inotify_init();
+#endif /* HAVE_SYS_INOTIFY_H */
+
+       initialisation(argc, argv);
+
        main_loop();
 
 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
@@ -9354,3 +9469,4 @@ static void signal_handler(int sig)
         * and do any signal processing there, NOT here. */
        g_signal_pending = sig;
 }
+