Better argument handling for rss/curl/weather.
[monky] / src / conky.c
index f82b522..136773c 100644 (file)
@@ -158,7 +158,7 @@ static void reload_config(void);
 static void generate_text_internal(char *, int, struct text_object,
                                    struct information *);
 static int extract_variable_text_internal(struct text_object *,
-                                          const char *, char);
+                                          const char *);
 
 static void print_version(void)
 {
@@ -208,11 +208,17 @@ static void print_version(void)
 #ifdef TCP_PORT_MONITOR
                   "  * portmon\n"
 #endif /* TCP_PORT_MONITOR */
+#ifdef HAVE_CURL
+                  "  * Curl\n"
+#endif /* HAVE_CURL */
 #ifdef RSS
                   "  * 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"
@@ -304,7 +310,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;
@@ -318,6 +324,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,11 +455,15 @@ int check_contains(char *f, char *s)
        return ret;
 }
 
+#define SECRIT_MULTILINE_CHAR '\x02'
+
 #ifdef X11
+
 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;
@@ -474,27 +489,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);
        }
 }
 
@@ -899,6 +911,11 @@ static void free_text_objects(struct text_object *root, int internal)
                        case OBJ_eve:
                                break;
 #endif
+#ifdef HAVE_CURL
+                       case OBJ_curl:
+                               free(data.curl.uri);
+                               break;
+#endif
 #ifdef RSS
                        case OBJ_rss:
                                free(data.rss.uri);
@@ -913,6 +930,7 @@ static void free_text_objects(struct text_object *root, int internal)
 #endif
 #ifdef HAVE_LUA
                        case OBJ_lua:
+                       case OBJ_lua_parse:
                        case OBJ_lua_bar:
 #ifdef X11
                        case OBJ_lua_graph:
@@ -945,6 +963,7 @@ static void free_text_objects(struct text_object *root, int internal)
                                free(data.execi.buffer);
                                break;
                        case OBJ_texeci:
+                               if (data.texeci.p_timed_thread) timed_thread_destroy(data.texeci.p_timed_thread, &data.texeci.p_timed_thread);
                                free(data.texeci.cmd);
                                free(data.texeci.buffer);
                                break;
@@ -1232,9 +1251,11 @@ 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)
+               const char *arg, long line, void **ifblock_opaque, void *free_at_crash)
 {
        // struct text_object *obj = new_text_object();
        struct text_object *obj = new_text_object_internal();
@@ -1246,8 +1267,6 @@ static struct text_object *construct_text_object(const char *s,
 #define OBJ_IF(a, n) if (strcmp(s, #a) == 0) { \
        obj->type = OBJ_##a; need_mask |= (1ULL << n); \
        obj_be_ifblock_if(ifblock_opaque, obj); {
-#define OBJ_THREAD(a, n) if (strcmp(s, #a) == 0 && allow_threaded) { \
-       obj->type = OBJ_##a; need_mask |= (1ULL << n); {
 #define END } } else
 
 #define SIZE_DEFAULTS(arg) { \
@@ -1644,28 +1663,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);
@@ -1799,7 +1829,7 @@ static struct text_object *construct_text_object(const char *s,
                        obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
                        obj->data.execi.buffer = malloc(text_buffer_size);
                }
-       END OBJ_THREAD(texeci, 0)
+       END OBJ(texeci, 0)
                        int n;
 
                        if (!arg || sscanf(arg, "%f %n", &obj->data.texeci.interval, &n) <= 0) {
@@ -2088,7 +2118,7 @@ static struct text_object *construct_text_object(const char *s,
                        obj->data.ifblock.s = strndup(arg, text_buffer_size);
                        obj->sub = malloc(sizeof(struct text_object));
                        extract_variable_text_internal(obj->sub,
-                                                      obj->data.ifblock.s, 0);
+                                                      obj->data.ifblock.s);
                }
        END OBJ_IF(if_match, 0)
                if (!arg) {
@@ -2098,7 +2128,7 @@ static struct text_object *construct_text_object(const char *s,
                        obj->data.ifblock.s = strndup(arg, text_buffer_size);
                        obj->sub = malloc(sizeof(struct text_object));
                        extract_variable_text_internal(obj->sub,
-                                                      obj->data.ifblock.s, 0);
+                                                      obj->data.ifblock.s);
                }
        END OBJ_IF(if_existing, 0)
                if (!arg) {
@@ -2573,7 +2603,7 @@ static struct text_object *construct_text_object(const char *s,
        END OBJ(apm_battery_life, 0)
        END OBJ(apm_battery_time, 0)
 #endif /* __FreeBSD__ */
-       END OBJ_THREAD(imap_unseen, 0)
+       END OBJ(imap_unseen, 0)
                if (arg) {
                        // proccss
                        obj->data.mail = parse_mail_args(IMAP_TYPE, arg);
@@ -2581,7 +2611,7 @@ static struct text_object *construct_text_object(const char *s,
                } else {
                        obj->char_b = 1;
                }
-       END OBJ_THREAD(imap_messages, 0)
+       END OBJ(imap_messages, 0)
                if (arg) {
                        // proccss
                        obj->data.mail = parse_mail_args(IMAP_TYPE, arg);
@@ -2589,7 +2619,7 @@ static struct text_object *construct_text_object(const char *s,
                } else {
                        obj->char_b = 1;
                }
-       END OBJ_THREAD(pop3_unseen, 0)
+       END OBJ(pop3_unseen, 0)
                if (arg) {
                        // proccss
                        obj->data.mail = parse_mail_args(POP3_TYPE, arg);
@@ -2597,7 +2627,7 @@ static struct text_object *construct_text_object(const char *s,
                } else {
                        obj->char_b = 1;
                }
-       END OBJ_THREAD(pop3_used, 0)
+       END OBJ(pop3_used, 0)
                if (arg) {
                        // proccss
                        obj->data.mail = parse_mail_args(POP3_TYPE, arg);
@@ -2794,61 +2824,105 @@ static struct text_object *construct_text_object(const char *s,
                        CRIT_ERR(obj, free_at_crash, "eve needs arguments: <userid> <apikey> <characterid>");
                }
 #endif
+#ifdef HAVE_CURL
+       END OBJ(curl, 0)
+               if (arg) {
+                       int argc;
+                       float interval;
+                       char *uri = (char *) malloc(128 * sizeof(char));
+
+                       argc = sscanf(arg, "%127s %f", uri, &interval);
+                       if (argc == 2) {
+                               obj->data.curl.uri = uri;
+                               obj->data.curl.interval = interval > 0 ? interval * 60 : 15*60;
+                       } else {
+                               ERR("wrong number of arguments for $curl");
+                       }
+               } else {
+                       CRIT_ERR(obj, free_at_crash, "curl needs arguments: <uri> <interval in minutes>");
+               }
+#endif
 #ifdef RSS
        END OBJ(rss, 0)
                if (arg) {
-                       int argc, delay, act_par;
+                       float interval;
+                       int argc, act_par;
                        unsigned int nrspaces = 0;
                        char *uri = (char *) malloc(128 * sizeof(char));
                        char *action = (char *) malloc(64 * sizeof(char));
 
-                       argc = sscanf(arg, "%127s %d %63s %d %u", uri, &delay, action,
+                       argc = sscanf(arg, "%127s %f %63s %d %u", uri, &interval, action,
                                        &act_par, &nrspaces);
-                       obj->data.rss.uri = uri;
-                       obj->data.rss.delay = delay;
-                       obj->data.rss.action = action;
-                       obj->data.rss.act_par = act_par;
-                       obj->data.rss.nrspaces = nrspaces;
-
-                       init_rss_info();
+                       if (argc == 5) {
+                               obj->data.rss.uri = uri;
+                               obj->data.rss.interval = interval > 0 ? interval * 60 : 15*60;
+                               obj->data.rss.action = action;
+                               obj->data.rss.act_par = act_par;
+                               obj->data.rss.nrspaces = nrspaces;
+                       } else {
+                               ERR("wrong number of arguments for $rss");
+                       }
                } else {
-                       CRIT_ERR(obj, free_at_crash, "rss needs arguments: <uri> <delay in minutes> <action> "
+                       CRIT_ERR(obj, free_at_crash, "rss needs arguments: <uri> <interval in minutes> <action> "
                                        "[act_par] [spaces in front]");
                }
 #endif
 #ifdef WEATHER
-       END OBJ_THREAD(weather, 0)
+       END OBJ(weather, 0)
                if (arg) {
-                       int argc, interval;
-                       char *icao = (char *) malloc(5 * sizeof(char));
+                       int argc;
+                       float interval;
+                       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 %f", uri, locID, data_type, &interval);
 
-                       //icao MUST BE upper-case
-                       tmp_p = icao;
-                       while (*tmp_p) {
-                               *tmp_p = toupper(*tmp_p);
-                               tmp_p++;
-                       }
+                       if (argc >= 3) {
+                               /* locID MUST BE upper-case */
+                               tmp_p = locID;
+                               while (*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");
+                               }
+
+                               obj->data.weather.uri = uri;
+                               obj->data.weather.data_type = data_type;
 
-                       strcat(uri, icao);
-                       strcat(uri, ".TXT");
-                       obj->data.weather.uri = uri;
+                               /* Limit the data retrieval interval to half hour min */
+                               if (interval < 30) {
+                                       interval = 30;
+                               }
 
-                       obj->data.weather.data_type = data_type;
+                               /* Convert to seconds */
+                               obj->data.weather.interval = interval * 60;
+                               free(locID);
 
-                       // The data retrieval interval is limited to half an hour
-                       if (interval < 30) {
-                               interval = 30;
+                               DBGP("weather: fetching %s from %s every %d seconds", \
+                                               data_type, uri, obj->data.weather.interval);
+                       } else {
+                               ERR("wrong number of arguments for $weather");
                        }
-                       obj->data.weather.interval = interval * 60; // convert to seconds
-                       free(icao);
                } 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
@@ -2929,33 +3003,39 @@ static struct text_object *construct_text_object(const char *s,
        END OBJ(blink, 0)
                if(arg) {
                        obj->sub = malloc(sizeof(struct text_object));
-                       extract_variable_text_internal(obj->sub, arg, 0);
+                       extract_variable_text_internal(obj->sub, arg);
                }else{
                        CRIT_ERR(obj, free_at_crash, "blink needs a argument");
                }
        END OBJ(to_bytes, 0)
                if(arg) {
                        obj->sub = malloc(sizeof(struct text_object));
-                       extract_variable_text_internal(obj->sub, arg, 0);
+                       extract_variable_text_internal(obj->sub, arg);
                }else{
                        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) {
                        sscanf(arg + n1, "%u %n", &obj->data.scroll.step, &n2);
-                       if(*(arg + n1 + n2)) {
+                       if (*(arg + n1 + n2)) {
                                n1 += n2;
-                       }else{
+                       } else {
                                obj->data.scroll.step = 1;
                        }
-                       obj->data.scroll.text = strndup(arg + n1, text_buffer_size);
+                       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,
-                                       obj->data.scroll.text, 0);
+                                       obj->data.scroll.text);
                } else {
                        CRIT_ERR(obj, free_at_crash, "scroll needs arguments: <length> [<step>] <text>");
                }
@@ -2999,9 +3079,9 @@ static struct text_object *construct_text_object(const char *s,
                                obj->data.combine.right[endvar[1] - startvar[1]] = 0;
 
                                obj->sub = malloc(sizeof(struct text_object));
-                               extract_variable_text_internal(obj->sub, obj->data.combine.left, 0);
+                               extract_variable_text_internal(obj->sub, obj->data.combine.left);
                                obj->sub->sub = malloc(sizeof(struct text_object));
-                               extract_variable_text_internal(obj->sub->sub, obj->data.combine.right, 0);
+                               extract_variable_text_internal(obj->sub->sub, obj->data.combine.right);
                        } else {
                                CRIT_ERR(obj, free_at_crash, "combine needs arguments: <text1> <text2>");
                        }
@@ -3325,7 +3405,7 @@ static size_t remove_comments(char *string)
        return folded;
 }
 
-static int extract_variable_text_internal(struct text_object *retval, const char *const_p, char allow_threaded)
+static int extract_variable_text_internal(struct text_object *retval, const char *const_p)
 {
        struct text_object *obj;
        char *p, *s, *orig_p;
@@ -3444,8 +3524,7 @@ static int extract_variable_text_internal(struct text_object *retval, const char
                                }
 
                                obj = construct_text_object(buf, arg,
-                                               line, allow_threaded,
-                                               &ifblock_opaque, orig_p);
+                                               line, &ifblock_opaque, orig_p);
                                if (obj != NULL) {
                                        append_object(retval, obj);
                                }
@@ -3497,12 +3576,12 @@ static void extract_variable_text(const char *p)
                text_buffer = 0;
        }
 
-       extract_variable_text_internal(&global_root_object, p, 1);
+       extract_variable_text_internal(&global_root_object, p);
 }
 
 void parse_conky_vars(struct text_object *root, char *txt, char *p, struct information *cur)
 {
-       extract_variable_text_internal(root, txt, 0);
+       extract_variable_text_internal(root, txt);
        generate_text_internal(p, max_user_text, *root, cur);
        return;
 }
@@ -3656,6 +3735,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)
 {
@@ -3676,7 +3771,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
  *
@@ -4170,14 +4265,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) {
@@ -4378,8 +4472,10 @@ static void generate_text_internal(char *p, int p_max_size,
                                        if (!obj->data.texeci.p_timed_thread) {
                                                ERR("Error creating texeci timed thread");
                                        }
-                                       timed_thread_register(obj->data.texeci.p_timed_thread,
-                                               &obj->data.texeci.p_timed_thread);
+                                       /*
+                                        * note that we don't register this thread with the
+                                        * timed_thread list, because we destroy it manually
+                                        */
                                        if (timed_thread_run(obj->data.texeci.p_timed_thread)) {
                                                ERR("Error running texeci timed thread");
                                        }
@@ -4570,84 +4666,31 @@ static void generate_text_internal(char *p, int p_max_size,
                                snprintf(p, p_max_size, "%s", skill);
                        }
 #endif
+#ifdef HAVE_CURL
+                       OBJ(curl) {
+                               if (obj->data.curl.uri != NULL) {
+                                       ccurl_process_info(p, p_max_size, obj->data.curl.uri, obj->data.curl.interval);
+                               } else {
+                                       ERR("error processing Curl data");
+                               }
+                       }
+#endif
 #ifdef RSS
                        OBJ(rss) {
-                               PRSS *data = get_rss_info(obj->data.rss.uri,
-                                       obj->data.rss.delay);
-                               char *str;
-
-                               if (data == NULL) {
-                                       snprintf(p, p_max_size, "prss: Error reading RSS data\n");
+                               if (obj->data.rss.uri != NULL) {
+                                       rss_process_info(p, p_max_size, obj->data.rss.uri, obj->data.rss.action, obj->data.rss.act_par, obj->data.rss.interval, obj->data.rss.nrspaces);
                                } else {
-                                       if (strcmp(obj->data.rss.action, "feed_title") == EQUAL) {
-                                               str = data->title;
-                                               // remove trailing new line if one exists
-                                               if (str[strlen(str) - 1] == '\n') {
-                                                       str[strlen(str) - 1] = 0;
-                                               }
-                                               snprintf(p, p_max_size, "%s", str);
-                                       } else if (strcmp(obj->data.rss.action, "item_title") == EQUAL) {
-                                               if (obj->data.rss.act_par < data->item_count) {
-                                                       str = data->items[obj->data.rss.act_par].title;
-                                                       // remove trailing new line if one exists
-                                                       if (str[strlen(str) - 1] == '\n') {
-                                                               str[strlen(str) - 1] = 0;
-                                                       }
-                                                       snprintf(p, p_max_size, "%s", str);
-                                               }
-                                       } else if (strcmp(obj->data.rss.action, "item_desc") == EQUAL) {
-                                               if (obj->data.rss.act_par < data->item_count) {
-                                                       str =
-                                                               data->items[obj->data.rss.act_par].description;
-                                                       // remove trailing new line if one exists
-                                                       if (str[strlen(str) - 1] == '\n') {
-                                                               str[strlen(str) - 1] = 0;
-                                                       }
-                                                       snprintf(p, p_max_size, "%s", str);
-                                               }
-                                       } else if (strcmp(obj->data.rss.action, "item_titles") == EQUAL) {
-                                               if (data->item_count > 0) {
-                                                       int itmp;
-                                                       int show;
-                                                       //'tmpspaces' is a string with spaces too be placed in front of each title
-                                                       char *tmpspaces = malloc(obj->data.rss.nrspaces + 1);
-                                                       memset(tmpspaces, ' ', obj->data.rss.nrspaces);
-                                                       tmpspaces[obj->data.rss.nrspaces]=0;
-
-                                                       p[0] = 0;
-
-                                                       if (obj->data.rss.act_par > data->item_count) {
-                                                               show = data->item_count;
-                                                       } else {
-                                                               show = obj->data.rss.act_par;
-                                                       }
-                                                       for (itmp = 0; itmp < show; itmp++) {
-                                                               PRSS_Item *item = &data->items[itmp];
-
-                                                               str = item->title;
-                                                               if (str) {
-                                                                       // don't add new line before first item
-                                                                       if (itmp > 0) {
-                                                                               strncat(p, "\n", p_max_size);
-                                                                       }
-                                                                       /* remove trailing new line if one exists,
-                                                                        * we have our own */
-                                                                       if (str[strlen(str) - 1] == '\n') {
-                                                                               str[strlen(str) - 1] = 0;
-                                                                       }
-                                                                       strncat(p, tmpspaces, p_max_size);
-                                                                       strncat(p, str, p_max_size);
-                                                               }
-                                                       }
-                                                       free(tmpspaces);
-                                               }
-                                       }
+                                       ERR("error processing RSS data");
                                }
                        }
 #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) {
+                                       weather_process_info(p, p_max_size, obj->data.weather.uri, obj->data.weather.data_type, obj->data.weather.interval);
+                               } else {
+                                       ERR("error processing weather data, check that you have a valid XOAP key if using XOAP.");
+                               }
                        }
 #endif
 #ifdef HAVE_LUA
@@ -4662,6 +4705,7 @@ static void generate_text_internal(char *p, int p_max_size,
                                char *str = llua_getstring(obj->data.s);
                                if (str) {
                                        evaluate(str, p);
+                                       free(str);
                                }
                        }
                        OBJ(lua_bar) {
@@ -5719,7 +5763,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){
@@ -5732,42 +5776,71 @@ 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);
+#ifdef X11
+                               //reset color when scroll is finished
+                               new_fg(p + strlen(p), obj->data.scroll.resetcolor);
+#endif
                        }
                        OBJ(combine) {
                                char buf[2][max_user_text];
@@ -5822,12 +5895,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) {
@@ -5969,6 +6042,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;
                }
@@ -6019,7 +6095,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;
@@ -6037,7 +6113,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)
@@ -6050,18 +6125,16 @@ static inline int get_string_width(const char *s)
        return strlen(s);
 }
 
-static inline int get_string_width_special(char *s)
-{
 #ifdef X11
+static int get_string_width_special(char *s, int special_index)
+{
        char *p, *final;
        int idx = 1;
        int width = 0;
        long i;
 
        if ((output_methods & TO_X) == 0) {
-#endif
                return (s) ? strlen(s) : 0;
-#ifdef X11
        }
 
        if (!s) {
@@ -6084,6 +6157,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++;
                }
@@ -6093,11 +6169,9 @@ static inline int get_string_width_special(char *s)
        }
        free(final);
        return width;
-#endif /* X11 */
 }
 
-#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)
@@ -6113,7 +6187,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;
@@ -6216,13 +6289,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) {
@@ -6266,6 +6339,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++;
        }
@@ -6279,6 +6360,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)
@@ -6391,36 +6473,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:
@@ -6643,6 +6728,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;
@@ -6684,6 +6770,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 *)
@@ -6735,6 +6822,7 @@ static void draw_line(char *s)
 
                                case OFFSET:
                                        w += specials[special_index].arg;
+                                       last_special_needed = special_index;
                                        break;
 
                                case VOFFSET:
@@ -6745,6 +6833,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:
@@ -6756,6 +6845,7 @@ static void draw_line(char *s)
                                                step = 10;
                                        }
                                        w = step - (cur_x - text_start_x - start) % step;
+                                       last_special_needed = special_index;
                                        break;
                                }
 
@@ -6764,7 +6854,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 "
@@ -6776,13 +6866,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; */
 
@@ -6794,24 +6886,47 @@ 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);
+               //'special_index - special_index' instead of 0 otherwise gcc complains about not using special_index when build without X11
+               return special_index - special_index;
+#ifdef X11
+       }
+
+       /* find specials and draw stuff */
+       return draw_each_line_inner(s, special_index, -1);
 #endif /* X11 */
 }
 
@@ -6844,7 +6959,6 @@ static void draw_text(void)
                }
 
                /* draw text */
-               special_index = 0;
        }
        setup_fonts();
 #endif /* X11 */
@@ -7500,11 +7614,14 @@ void clean_up(void *memtofree1, void* memtofree2)
 #ifdef TCP_PORT_MONITOR
        tcp_portmon_clear();
 #endif
+#ifdef HAVE_CURL
+       ccurl_free_info();
+#endif
 #ifdef RSS
-       free_rss_info();
+       rss_free_info();
 #endif
 #ifdef WEATHER
-       free_weather_info();
+       weather_free_info();
 #endif
 #ifdef HAVE_LUA
        llua_close();
@@ -7601,6 +7718,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 */
 
@@ -7728,7 +7846,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
@@ -8500,7 +8618,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) {
@@ -8865,7 +8983,7 @@ 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();
                        }
@@ -8885,6 +9003,42 @@ static void load_config_file_x11(const char *f)
 }
 #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"
                        PACKAGE_NAME" is a system monitor that renders text on desktop or to own transparent\n"
@@ -9110,7 +9264,7 @@ void initialisation(int argc, char **argv) {
                                fprintf(stderr, PACKAGE_NAME": forked to background, pid is %d\n",
                                        pid);
                                fflush(stderr);
-                               return;
+                               exit(EXIT_SUCCESS);
                }
        }
 
@@ -9268,6 +9422,12 @@ int main(int argc, char **argv)
 #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 */
@@ -9290,3 +9450,4 @@ static void signal_handler(int sig)
         * and do any signal processing there, NOT here. */
        g_signal_pending = sig;
 }
+