diff --git a/src/Makefile.am b/src/Makefile.am index a8276ac3..36ab8f51 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -171,6 +171,8 @@ conky_SOURCES = \ $(weather) \ $(lua) \ $(solaris) \ + template.c \ + template.h \ timed_thread.c \ timed_thread.h \ mboxscan.c \ @@ -239,6 +241,8 @@ EXTRA_DIST = \ specials.h \ tailhead.c \ tailhead.h \ + template.c \ + template.h \ top.h \ diskio.h \ x11.c \ diff --git a/src/conky.c b/src/conky.c index 59987dad..40cb317b 100644 --- a/src/conky.c +++ b/src/conky.c @@ -87,6 +87,7 @@ #include "mboxscan.h" #include "specials.h" #include "temphelper.h" +#include "template.h" #include "tailhead.h" #include "top.h" @@ -381,13 +382,6 @@ static int sensor_device; long color0, color1, color2, color3, color4, color5, color6, color7, color8, color9; -static char *template[MAX_TEMPLATES]; - -char **get_templates(void) -{ - return template; -} - /* maximum size of config TEXT buffer, i.e. below TEXT line. */ unsigned int max_user_text; @@ -4886,12 +4880,7 @@ void clean_up(void *memtofree1, void* memtofree2) #endif /* X11 */ - for (i = 0; i < MAX_TEMPLATES; i++) { - if (template[i]) { - free(template[i]); - template[i] = NULL; - } - } + free_templates(); free_text_objects(&global_root_object, 0); if (tmpstring1) { @@ -5035,7 +5024,6 @@ static void set_default_configurations_for_x(void) static void set_default_configurations(void) { - int i; #ifdef MPD char *mpd_env_host; char *mpd_env_port; @@ -5141,11 +5129,7 @@ static void set_default_configurations(void) info.x11.desktop.name = NULL; #endif /* X11 */ - for (i = 0; i < MAX_TEMPLATES; i++) { - if (template[i]) - free(template[i]); - template[i] = strdup(""); - } + free_templates(); free(current_mail_spool); { @@ -5463,12 +5447,8 @@ char load_config_file(const char *f) #endif /* X11 */ #define TEMPLATE_CONF(n) \ CONF("template"#n) { \ - if (value) { \ - free(template[n]); \ - template[n] = strdup(value); \ - } else { \ + if (set_template(n, value)) \ CONF_ERR; \ - } \ } TEMPLATE_CONF(0) TEMPLATE_CONF(1) @@ -6604,7 +6584,7 @@ int main(int argc, char **argv) max_user_text = MAX_USER_TEXT_DEFAULT; current_config = 0; memset(&info, 0, sizeof(info)); - memset(template, 0, sizeof(template)); + free_templates(); clear_net_stats(); #ifdef TCP_PORT_MONITOR diff --git a/src/core.c b/src/core.c index aa7c8e69..bfc636ed 100644 --- a/src/core.c +++ b/src/core.c @@ -46,6 +46,7 @@ #include "mboxscan.h" #include "specials.h" #include "temphelper.h" +#include "template.h" #include "tailhead.h" #include "top.h" @@ -2173,197 +2174,6 @@ struct text_object *construct_text_object(const char *s, const char *arg, long return obj; } -/* backslash_escape - do the actual substitution task for template objects - * - * The field templates is used for substituting the \N occurences. Set it to - * NULL to leave them as they are. - */ -static char *backslash_escape(const char *src, char **templates, unsigned int template_count) -{ - char *src_dup; - const char *p; - unsigned int dup_idx = 0, dup_len; - - dup_len = strlen(src) + 1; - src_dup = malloc(dup_len * sizeof(char)); - - p = src; - while (*p) { - switch (*p) { - case '\\': - if (!*(p + 1)) - break; - if (*(p + 1) == '\\') { - src_dup[dup_idx++] = '\\'; - p++; - } else if (*(p + 1) == ' ') { - src_dup[dup_idx++] = ' '; - p++; - } else if (*(p + 1) == 'n') { - src_dup[dup_idx++] = '\n'; - p++; - } else if (templates) { - unsigned int tmpl_num; - int digits; - if ((sscanf(p + 1, "%u%n", &tmpl_num, &digits) <= 0) || - (tmpl_num > template_count)) - break; - dup_len += strlen(templates[tmpl_num - 1]); - src_dup = realloc(src_dup, dup_len * sizeof(char)); - sprintf(src_dup + dup_idx, "%s", templates[tmpl_num - 1]); - dup_idx += strlen(templates[tmpl_num - 1]); - p += digits; - } - break; - default: - src_dup[dup_idx++] = *p; - break; - } - p++; - } - src_dup[dup_idx] = '\0'; - src_dup = realloc(src_dup, (strlen(src_dup) + 1) * sizeof(char)); - return src_dup; -} - -/* handle_template_object - core logic of the template object - * - * use config variables like this: - * template1 = "$\1\2" - * template2 = "\1: ${fs_bar 4,100 \2} ${fs_used \2} / ${fs_size \2}" - * - * and use them like this: - * ${template1 node name} - * ${template2 root /} - * ${template2 cdrom /mnt/cdrom} - */ -static char *handle_template(const char *tmpl, const char *args) -{ - char *args_dup = NULL; - char *p, *p_old; - char **argsp = NULL; - unsigned int argcnt = 0, template_idx, i; - char *eval_text; - - if ((sscanf(tmpl, "template%u", &template_idx) != 1) || - (template_idx >= MAX_TEMPLATES)) - return NULL; - - if(args) { - args_dup = strdup(args); - p = args_dup; - while (*p) { - while (*p && (*p == ' ' && (p == args_dup || *(p - 1) != '\\'))) - p++; - if (p > args_dup && *(p - 1) == '\\') - p--; - p_old = p; - while (*p && (*p != ' ' || (p > args_dup && *(p - 1) == '\\'))) - p++; - if (*p) { - (*p) = '\0'; - p++; - } - argsp = realloc(argsp, ++argcnt * sizeof(char *)); - argsp[argcnt - 1] = p_old; - } - for (i = 0; i < argcnt; i++) { - char *tmp; - tmp = backslash_escape(argsp[i], NULL, 0); - DBGP2("%s: substituted arg '%s' to '%s'", tmpl, argsp[i], tmp); - argsp[i] = tmp; - } - } - - eval_text = backslash_escape(get_templates()[template_idx], argsp, argcnt); - DBGP("substituted %s, output is '%s'", tmpl, eval_text); - free(args_dup); - for (i = 0; i < argcnt; i++) - free(argsp[i]); - free(argsp); - return eval_text; -} - -static char *find_and_replace_templates(const char *inbuf) -{ - char *outbuf, *indup, *p, *o, *templ, *args, *tmpl_out; - int stack, outlen; - - outlen = strlen(inbuf) + 1; - o = outbuf = calloc(outlen, sizeof(char)); - memset(outbuf, 0, outlen * sizeof(char)); - - p = indup = strdup(inbuf); - while (*p) { - while (*p && *p != '$') - *(o++) = *(p++); - - if (!(*p)) - break; - - if (strncmp(p, "$template", 9) && strncmp(p, "${template", 10)) { - *(o++) = *(p++); - continue; - } - - if (*(p + 1) == '{') { - p += 2; - templ = p; - while (*p && !isspace(*p) && *p != '{' && *p != '}') - p++; - if (*p == '}') - args = NULL; - else - args = p; - - stack = 1; - while (*p && stack > 0) { - if (*p == '{') - stack++; - else if (*p == '}') - stack--; - p++; - } - if (stack == 0) { - // stack is empty. that means the previous char was }, so we zero it - *(p - 1) = '\0'; - } else { - // we ran into the end of string without finding a closing }, bark - CRIT_ERR(NULL, NULL, "cannot find a closing '}' in template expansion"); - } - } else { - templ = p + 1; - while (*p && !isspace(*p)) - p++; - args = NULL; - } - tmpl_out = handle_template(templ, args); - if (tmpl_out) { - outlen += strlen(tmpl_out); - *o = '\0'; - outbuf = realloc(outbuf, outlen * sizeof(char)); - strcat (outbuf, tmpl_out); - free(tmpl_out); - o = outbuf + strlen(outbuf); - } else { - NORM_ERR("failed to handle template '%s' with args '%s'", templ, args); - } - } - *o = '\0'; - outbuf = realloc(outbuf, (strlen(outbuf) + 1) * sizeof(char)); - free(indup); - return outbuf; -} - -static int text_contains_templates(const char *text) -{ - if (strcasestr(text, "${template") != NULL) - return 1; - if (strcasestr(text, "$template") != NULL) - return 1; - return 0; -} - /* * - assumes that *string is '#' * - removes the part from '#' to the end of line ('\n' or '\0') diff --git a/src/template.c b/src/template.c new file mode 100644 index 00000000..b98166ac --- /dev/null +++ b/src/template.c @@ -0,0 +1,270 @@ +/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- + * + * Conky, a system monitor, based on torsmo + * + * Any original torsmo code is licensed under the BSD license + * + * All code written since the fork of torsmo is licensed under the GPL + * + * Please see COPYING for details + * + * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen + * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al. + * (see AUTHORS) + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * vim: ts=4 sw=4 noet ai cindent syntax=c + * + */ +#include "conky.h" +#include "logging.h" +#include +#include +#include +/* The templates defined by the user. + * + * This is a 1 to 1 mapping from templateN config option to template[N] field. */ +static char *template[MAX_TEMPLATES]; + +/* free all templates + * + * On first invocation, just memset all pointers to zero, so this function can + * be used when initialising data upon startup. */ +void free_templates(void) +{ + int i; + static int initialised = 0; + + if (!initialised) { + memset(template, 0, MAX_TEMPLATES * sizeof(char *)); + initialised = 1; + return; + } + + for (i = 0; i < MAX_TEMPLATES; i++) { + if (template[i]) { + free(template[i]); + template[i] = NULL; + } + } +} + +/* set the value of template at index n + * + * Returns non-zero on illegal arguments passed, zero otherwise. */ +int set_template(int n, const char *val) +{ + if (n < 0 || n >= MAX_TEMPLATES || !val) + return 1; + if (template[n]) + free(template[n]); + template[n] = strdup(val); + return 0; +} + +/* backslash_escape - do the actual substitution task for template objects + * + * The field templates is used for substituting the \N occurences. Set it to + * NULL to leave them as they are. + */ +static char *backslash_escape(const char *src, char **templates, unsigned int template_count) +{ + char *src_dup; + const char *p; + unsigned int dup_idx = 0, dup_len; + + dup_len = strlen(src) + 1; + src_dup = malloc(dup_len * sizeof(char)); + + p = src; + while (*p) { + switch (*p) { + case '\\': + if (!*(p + 1)) + break; + if (*(p + 1) == '\\') { + src_dup[dup_idx++] = '\\'; + p++; + } else if (*(p + 1) == ' ') { + src_dup[dup_idx++] = ' '; + p++; + } else if (*(p + 1) == 'n') { + src_dup[dup_idx++] = '\n'; + p++; + } else if (templates) { + unsigned int tmpl_num; + int digits; + if ((sscanf(p + 1, "%u%n", &tmpl_num, &digits) <= 0) || + (tmpl_num > template_count)) + break; + dup_len += strlen(templates[tmpl_num - 1]); + src_dup = realloc(src_dup, dup_len * sizeof(char)); + sprintf(src_dup + dup_idx, "%s", templates[tmpl_num - 1]); + dup_idx += strlen(templates[tmpl_num - 1]); + p += digits; + } + break; + default: + src_dup[dup_idx++] = *p; + break; + } + p++; + } + src_dup[dup_idx] = '\0'; + src_dup = realloc(src_dup, (strlen(src_dup) + 1) * sizeof(char)); + return src_dup; +} + +/* handle_template_object - core logic of the template object + * + * use config variables like this: + * template1 = "$\1\2" + * template2 = "\1: ${fs_bar 4,100 \2} ${fs_used \2} / ${fs_size \2}" + * + * and use them like this: + * ${template1 node name} + * ${template2 root /} + * ${template2 cdrom /mnt/cdrom} + */ +static char *handle_template(const char *tmpl, const char *args) +{ + char *args_dup = NULL; + char *p, *p_old; + char **argsp = NULL; + unsigned int argcnt = 0, template_idx, i; + char *eval_text; + + if ((sscanf(tmpl, "template%u", &template_idx) != 1) || + (template_idx >= MAX_TEMPLATES)) + return NULL; + + if(args) { + args_dup = strdup(args); + p = args_dup; + while (*p) { + while (*p && (*p == ' ' && (p == args_dup || *(p - 1) != '\\'))) + p++; + if (p > args_dup && *(p - 1) == '\\') + p--; + p_old = p; + while (*p && (*p != ' ' || (p > args_dup && *(p - 1) == '\\'))) + p++; + if (*p) { + (*p) = '\0'; + p++; + } + argsp = realloc(argsp, ++argcnt * sizeof(char *)); + argsp[argcnt - 1] = p_old; + } + for (i = 0; i < argcnt; i++) { + char *tmp; + tmp = backslash_escape(argsp[i], NULL, 0); + DBGP2("%s: substituted arg '%s' to '%s'", tmpl, argsp[i], tmp); + argsp[i] = tmp; + } + } + + eval_text = backslash_escape(template[template_idx], argsp, argcnt); + DBGP("substituted %s, output is '%s'", tmpl, eval_text); + free(args_dup); + for (i = 0; i < argcnt; i++) + free(argsp[i]); + free(argsp); + return eval_text; +} + +/* Search inbuf and replace all found template object references + * with the substituted value. */ +char *find_and_replace_templates(const char *inbuf) +{ + char *outbuf, *indup, *p, *o, *templ, *args, *tmpl_out; + int stack, outlen; + + outlen = strlen(inbuf) + 1; + o = outbuf = calloc(outlen, sizeof(char)); + memset(outbuf, 0, outlen * sizeof(char)); + + p = indup = strdup(inbuf); + while (*p) { + while (*p && *p != '$') + *(o++) = *(p++); + + if (!(*p)) + break; + + if (strncmp(p, "$template", 9) && strncmp(p, "${template", 10)) { + *(o++) = *(p++); + continue; + } + + if (*(p + 1) == '{') { + p += 2; + templ = p; + while (*p && !isspace(*p) && *p != '{' && *p != '}') + p++; + if (*p == '}') + args = NULL; + else + args = p; + + stack = 1; + while (*p && stack > 0) { + if (*p == '{') + stack++; + else if (*p == '}') + stack--; + p++; + } + if (stack == 0) { + // stack is empty. that means the previous char was }, so we zero it + *(p - 1) = '\0'; + } else { + // we ran into the end of string without finding a closing }, bark + CRIT_ERR(NULL, NULL, "cannot find a closing '}' in template expansion"); + } + } else { + templ = p + 1; + while (*p && !isspace(*p)) + p++; + args = NULL; + } + tmpl_out = handle_template(templ, args); + if (tmpl_out) { + outlen += strlen(tmpl_out); + *o = '\0'; + outbuf = realloc(outbuf, outlen * sizeof(char)); + strcat (outbuf, tmpl_out); + free(tmpl_out); + o = outbuf + strlen(outbuf); + } else { + NORM_ERR("failed to handle template '%s' with args '%s'", templ, args); + } + } + *o = '\0'; + outbuf = realloc(outbuf, (strlen(outbuf) + 1) * sizeof(char)); + free(indup); + return outbuf; +} + +/* check text for any template object references */ +int text_contains_templates(const char *text) +{ + if (strcasestr(text, "${template") != NULL) + return 1; + if (strcasestr(text, "$template") != NULL) + return 1; + return 0; +} + diff --git a/src/template.h b/src/template.h new file mode 100644 index 00000000..81b3e9e6 --- /dev/null +++ b/src/template.h @@ -0,0 +1,41 @@ +/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- + * + * Conky, a system monitor, based on torsmo + * + * Any original torsmo code is licensed under the BSD license + * + * All code written since the fork of torsmo is licensed under the GPL + * + * Please see COPYING for details + * + * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen + * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al. + * (see AUTHORS) + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * vim: ts=4 sw=4 noet ai cindent syntax=c + * + */ + +#ifndef _TEMPLATE_H +#define _TEMPLATE_H + +void free_templates(void); +int set_template(int, const char *); + +char *find_and_replace_templates(const char *); +int text_contains_templates(const char *); + +#endif /* _TEMPLATE_H */