Fix removing the config and sending a SIGUSR1 results in segfault
[monky] / src / template.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Any original torsmo code is licensed under the BSD license
7  *
8  * All code written since the fork of torsmo is licensed under the GPL
9  *
10  * Please see COPYING for details
11  *
12  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13  * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
14  *      (see AUTHORS)
15  * All rights reserved.
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30 #include "conky.h"
31 #include "logging.h"
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <string.h>
35 /* The templates defined by the user.
36  *
37  * This is a 1 to 1 mapping from templateN config option to template[N] field. */
38 static char *template[MAX_TEMPLATES];
39
40 /* free all templates
41  *
42  * On first invocation, just memset all pointers to zero, so this function can
43  * be used when initialising data upon startup. */
44 void free_templates(void)
45 {
46         int i;
47         static int initialised = 0;
48
49         if (!initialised) {
50                 memset(template, 0, MAX_TEMPLATES * sizeof(char *));
51                 initialised = 1;
52                 return;
53         }
54
55         for (i = 0; i < MAX_TEMPLATES; i++) {
56                 if (template[i]) {
57                         free(template[i]);
58                         template[i] = NULL;
59                 }
60         }
61 }
62
63 /* set the value of template at index n
64  *
65  * Returns non-zero on illegal arguments passed, zero otherwise. */
66 int set_template(int n, const char *val)
67 {
68         if (n < 0 || n >= MAX_TEMPLATES || !val)
69                 return 1;
70         if (template[n])
71                 free(template[n]);
72         template[n] = strdup(val);
73         return 0;
74 }
75
76 /* backslash_escape - do the actual substitution task for template objects
77  *
78  * The field templates is used for substituting the \N occurences. Set it to
79  * NULL to leave them as they are.
80  */
81 static char *backslash_escape(const char *src, char **templates, unsigned int template_count)
82 {
83         char *src_dup;
84         const char *p;
85         unsigned int dup_idx = 0, dup_len;
86
87         dup_len = strlen(src) + 1;
88         src_dup = malloc(dup_len * sizeof(char));
89
90         p = src;
91         while (*p) {
92                 switch (*p) {
93                 case '\\':
94                         if (!*(p + 1))
95                                 break;
96                         if (*(p + 1) == '\\') {
97                                 src_dup[dup_idx++] = '\\';
98                                 p++;
99                         } else if (*(p + 1) == ' ') {
100                                 src_dup[dup_idx++] = ' ';
101                                 p++;
102                         } else if (*(p + 1) == 'n') {
103                                 src_dup[dup_idx++] = '\n';
104                                 p++;
105                         } else if (templates) {
106                                 unsigned int tmpl_num;
107                                 int digits;
108                                 if ((sscanf(p + 1, "%u%n", &tmpl_num, &digits) <= 0) ||
109                                     (tmpl_num > template_count))
110                                         break;
111                                 dup_len += strlen(templates[tmpl_num - 1]);
112                                 src_dup = realloc(src_dup, dup_len * sizeof(char));
113                                 sprintf(src_dup + dup_idx, "%s", templates[tmpl_num - 1]);
114                                 dup_idx += strlen(templates[tmpl_num - 1]);
115                                 p += digits;
116                         }
117                         break;
118                 default:
119                         src_dup[dup_idx++] = *p;
120                         break;
121                 }
122                 p++;
123         }
124         src_dup[dup_idx] = '\0';
125         src_dup = realloc(src_dup, (strlen(src_dup) + 1) * sizeof(char));
126         return src_dup;
127 }
128
129 /* handle_template_object - core logic of the template object
130  *
131  * use config variables like this:
132  * template1 = "$\1\2"
133  * template2 = "\1: ${fs_bar 4,100 \2} ${fs_used \2} / ${fs_size \2}"
134  *
135  * and use them like this:
136  * ${template1 node name}
137  * ${template2 root /}
138  * ${template2 cdrom /mnt/cdrom}
139  */
140 static char *handle_template(const char *tmpl, const char *args)
141 {
142         char *args_dup = NULL;
143         char *p, *p_old;
144         char **argsp = NULL;
145         unsigned int argcnt = 0, template_idx, i;
146         char *eval_text;
147
148         if ((sscanf(tmpl, "template%u", &template_idx) != 1) ||
149             (template_idx >= MAX_TEMPLATES))
150                 return NULL;
151
152         if(args) {
153                 args_dup = strdup(args);
154                 p = args_dup;
155                 while (*p) {
156                         while (*p && (*p == ' ' && (p == args_dup || *(p - 1) != '\\')))
157                                 p++;
158                         if (p > args_dup && *(p - 1) == '\\')
159                                 p--;
160                         p_old = p;
161                         while (*p && (*p != ' ' || (p > args_dup && *(p - 1) == '\\')))
162                                 p++;
163                         if (*p) {
164                                 (*p) = '\0';
165                                 p++;
166                         }
167                         argsp = realloc(argsp, ++argcnt * sizeof(char *));
168                         argsp[argcnt - 1] = p_old;
169                 }
170                 for (i = 0; i < argcnt; i++) {
171                         char *tmp;
172                         tmp = backslash_escape(argsp[i], NULL, 0);
173                         DBGP2("%s: substituted arg '%s' to '%s'", tmpl, argsp[i], tmp);
174                         argsp[i] = tmp;
175                 }
176         }
177
178         eval_text = backslash_escape(template[template_idx], argsp, argcnt);
179         DBGP("substituted %s, output is '%s'", tmpl, eval_text);
180         free(args_dup);
181         for (i = 0; i < argcnt; i++)
182                 free(argsp[i]);
183         free(argsp);
184         return eval_text;
185 }
186
187 /* Search inbuf and replace all found template object references
188  * with the substituted value. */
189 char *find_and_replace_templates(const char *inbuf)
190 {
191         char *outbuf, *indup, *p, *o, *templ, *args, *tmpl_out;
192         int stack, outlen;
193
194         outlen = strlen(inbuf) + 1;
195         o = outbuf = calloc(outlen, sizeof(char));
196         memset(outbuf, 0, outlen * sizeof(char));
197
198         p = indup = strdup(inbuf);
199         while (*p) {
200                 while (*p && *p != '$')
201                         *(o++) = *(p++);
202
203                 if (!(*p))
204                         break;
205
206                 if (strncmp(p, "$template", strlen("$template")) && strncmp(p, "${template", strlen("${template"))) {
207                         *(o++) = *(p++);
208                         continue;
209                 }
210
211                 if (*(p + 1) == '{') {
212                         p += 2;
213                         templ = p;
214                         while (*p && !isspace(*p) && *p != '{' && *p != '}')
215                                 p++;
216                         if (*p == '}')
217                                 args = NULL;
218                         else
219                                 args = p;
220
221                         stack = 1;
222                         while (*p && stack > 0) {
223                                 if (*p == '{')
224                                         stack++;
225                                 else if (*p == '}')
226                                         stack--;
227                                 p++;
228                         }
229                         if (stack == 0) {
230                                 // stack is empty. that means the previous char was }, so we zero it
231                                 *(p - 1) = '\0';
232                         } else {
233                                 // we ran into the end of string without finding a closing }, bark
234                                 CRIT_ERR(NULL, NULL, "cannot find a closing '}' in template expansion");
235                         }
236                 } else {
237                         templ = p + 1;
238                         p += strlen("$template");
239                         while (*p && isdigit(*p))
240                                 p++;
241                         args = NULL;
242                 }
243                 tmpl_out = handle_template(templ, args);
244                 if (tmpl_out) {
245                         outlen += strlen(tmpl_out);
246                         *o = '\0';
247                         outbuf = realloc(outbuf, outlen * sizeof(char));
248                         strcat (outbuf, tmpl_out);
249                         free(tmpl_out);
250                         o = outbuf + strlen(outbuf);
251                 } else {
252                         NORM_ERR("failed to handle template '%s' with args '%s'", templ, args);
253                 }
254         }
255         *o = '\0';
256         outbuf = realloc(outbuf, (strlen(outbuf) + 1) * sizeof(char));
257         free(indup);
258         return outbuf;
259 }
260
261 /* check text for any template object references */
262 int text_contains_templates(const char *text)
263 {
264         if (strcasestr(text, "${template") != NULL)
265                 return 1;
266         if (strcasestr(text, "$template") != NULL)
267                 return 1;
268         return 0;
269 }
270