3 * Bacula Director -- expand.c -- does variable expansion
4 * in particular for the LabelFormat specification.
6 * Kern Sibbald, June MMIII
11 Copyright (C) 2003-2004 Kern Sibbald and John Walker
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of
16 the License, or (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
23 You should have received a copy of the GNU General Public
24 License along with this program; if not, write to the Free
25 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
35 static int date_item(JCR *jcr, int code,
36 const char **val_ptr, int *val_len, int *val_size)
39 time_t now = time(NULL);
40 (void)localtime_r(&now, &tm);
46 val = tm.tm_year + 1900;
63 case 7: /* Week day */
67 bsnprintf(buf, sizeof(buf), "%d", val);
68 *val_ptr = bstrdup(buf);
69 *val_len = strlen(buf);
70 *val_size = *val_len + 1;
74 static int job_item(JCR *jcr, int code,
75 const char **val_ptr, int *val_len, int *val_size)
77 const char *str = " ";
82 str = jcr->job->name();
84 case 2: /* Director's name */
88 str = job_level_to_str(jcr->JobLevel);
91 str = job_type_to_str(jcr->JobType);
94 bsnprintf(buf, sizeof(buf), "%d", jcr->JobId);
98 str = jcr->client->name();
103 case 7: /* NumVols */
104 bsnprintf(buf, sizeof(buf), "%d", jcr->NumVols);
108 str = jcr->pool->name();
110 case 9: /* Storage */
112 str = jcr->wstore->name();
114 str = jcr->rstore->name();
117 case 10: /* Catalog */
118 str = jcr->catalog->name();
120 case 11: /* MediaType */
122 str = jcr->wstore->media_type;
124 str = jcr->rstore->media_type;
127 case 12: /* JobName */
131 *val_ptr = bstrdup(str);
132 *val_len = strlen(str);
133 *val_size = *val_len + 1;
137 struct s_built_in_vars {const char *var_name; int code; int (*func)(JCR *jcr, int code,
138 const char **val_ptr, int *val_len, int *val_size);};
141 * Table of build in variables
143 static struct s_built_in_vars built_in_vars[] = {
144 { NT_("Year"), 1, date_item},
145 { NT_("Month"), 2, date_item},
146 { NT_("Day"), 3, date_item},
147 { NT_("Hour"), 4, date_item},
148 { NT_("Minute"), 5, date_item},
149 { NT_("Second"), 6, date_item},
150 { NT_("WeekDay"), 7, date_item},
152 { NT_("Job"), 1, job_item},
153 { NT_("Dir"), 2, job_item},
154 { NT_("Level"), 3, job_item},
155 { NT_("Type"), 4, job_item},
156 { NT_("JobId"), 5, job_item},
157 { NT_("Client"), 6, job_item},
158 { NT_("NumVols"), 7, job_item},
159 { NT_("Pool"), 8, job_item},
160 { NT_("Storage"), 9, job_item},
161 { NT_("Catalog"), 10, job_item},
162 { NT_("MediaType"), 11, job_item},
163 { NT_("JobName"), 12, job_item},
170 * Search the table of built-in variables, and if found,
171 * call the appropriate subroutine to do the work.
173 static var_rc_t lookup_built_in_var(var_t *ctx, void *my_ctx,
174 const char *var_ptr, int var_len, int var_index,
175 const char **val_ptr, int *val_len, int *val_size)
177 JCR *jcr = (JCR *)my_ctx;
180 for (int i=0; _(built_in_vars[i].var_name); i++) {
181 if (strncmp(_(built_in_vars[i].var_name), var_ptr, var_len) == 0) {
182 stat = (*built_in_vars[i].func)(jcr, built_in_vars[i].code,
183 val_ptr, val_len, val_size);
190 return VAR_ERR_UNDEFINED_VARIABLE;
195 * Search counter variables
197 static var_rc_t lookup_counter_var(var_t *ctx, void *my_ctx,
198 const char *var_ptr, int var_len, int var_inc, int var_index,
199 const char **val_ptr, int *val_len, int *val_size)
202 var_rc_t stat = VAR_ERR_UNDEFINED_VARIABLE;
204 if (var_len > (int)sizeof(buf) - 1) {
205 return VAR_ERR_OUT_OF_MEMORY;
207 memcpy(buf, var_ptr, var_len);
210 for (COUNTER *counter=NULL; (counter = (COUNTER *)GetNextRes(R_COUNTER, (RES *)counter)); ) {
211 if (strcmp(counter->name(), buf) == 0) {
212 Dmsg2(100, "Counter=%s val=%d\n", buf, counter->CurrentValue);
213 /* -1 => return size of array */
214 if (var_index == -1) {
215 bsnprintf(buf, sizeof(buf), "%d", counter->CurrentValue);
216 *val_len = bsnprintf(buf, sizeof(buf), "%d", strlen(buf));
218 *val_size = 0; /* don't try to free val_ptr */
221 bsnprintf(buf, sizeof(buf), "%d", counter->CurrentValue);
222 *val_ptr = bstrdup(buf);
223 *val_len = strlen(buf);
224 *val_size = *val_len + 1;
226 if (var_inc) { /* increment the variable? */
227 if (counter->CurrentValue == counter->MaxValue) {
228 counter->CurrentValue = counter->MinValue;
230 counter->CurrentValue++;
232 if (counter->Catalog) { /* update catalog if need be */
234 JCR *jcr = (JCR *)my_ctx;
235 memset(&cr, 0, sizeof(cr));
236 bstrncpy(cr.Counter, counter->name(), sizeof(cr.Counter));
237 cr.MinValue = counter->MinValue;
238 cr.MaxValue = counter->MaxValue;
239 cr.CurrentValue = counter->CurrentValue;
240 Dmsg1(100, "New value=%d\n", cr.CurrentValue);
241 if (counter->WrapCounter) {
242 bstrncpy(cr.WrapCounter, counter->WrapCounter->name(), sizeof(cr.WrapCounter));
244 cr.WrapCounter[0] = 0;
246 if (!db_update_counter_record(jcr, jcr->db, &cr)) {
247 Jmsg(jcr, M_ERROR, 0, _("Count not update counter %s: ERR=%s\n"),
248 counter->name(), db_strerror(jcr->db));
262 * Called here from "core" expand code to look up a variable
264 static var_rc_t lookup_var(var_t *ctx, void *my_ctx,
265 const char *var_ptr, int var_len, int var_inc, int var_index,
266 const char **val_ptr, int *val_len, int *val_size)
268 char buf[MAXSTRING], *val, *p, *v;
272 /* Note, if val_size > 0 and val_ptr!=NULL, the core code will free() it */
273 if ((stat = lookup_built_in_var(ctx, my_ctx, var_ptr, var_len, var_index,
274 val_ptr, val_len, val_size)) == VAR_OK) {
278 if ((stat = lookup_counter_var(ctx, my_ctx, var_ptr, var_len, var_inc, var_index,
279 val_ptr, val_len, val_size)) == VAR_OK) {
283 /* Look in environment */
284 if (var_len > (int)sizeof(buf) - 1) {
285 return VAR_ERR_OUT_OF_MEMORY;
287 memcpy(buf, var_ptr, var_len + 1);
289 Dmsg1(100, "Var=%s\n", buf);
291 if ((val = getenv(buf)) == NULL) {
292 return VAR_ERR_UNDEFINED_VARIABLE;
294 /* He wants to index the "array" */
296 /* Find the size of the "array"
297 * each element is separated by a |
299 for (p = val; *p; p++) {
304 Dmsg3(100, "For %s, reqest index=%d have=%d\n",
305 buf, var_index, count);
307 /* -1 => return size of array */
308 if (var_index == -1) {
310 if (count == 1) { /* if not array */
311 len = strlen(val); /* return length of string */
313 len = count; /* else return # array items */
315 *val_len = bsnprintf(buf, sizeof(buf), "%d", len);
317 *val_size = 0; /* don't try to free val_ptr */
322 if (var_index < -1 || var_index > --count) {
323 // return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
324 return VAR_ERR_UNDEFINED_VARIABLE;
326 /* Now find the particular item (var_index) he wants */
330 if (count < var_index) {
339 if (p-val > (int)sizeof(buf) - 1) {
340 return VAR_ERR_OUT_OF_MEMORY;
342 Dmsg2(100, "val=%s len=%d\n", val, p-val);
343 /* Make a copy of item, and pass it back */
344 v = (char *)malloc(p-val+1);
345 memcpy(v, val, p-val);
350 Dmsg1(100, "v=%s\n", v);
355 * Called here to do a special operation on a variable
356 * op_ptr points to the special operation code (not EOS terminated)
357 * arg_ptr points to argument to special op code
358 * val_ptr points to the value string
359 * out_ptr points to string to be returned
361 static var_rc_t operate_var(var_t *var, void *my_ctx,
362 const char *op_ptr, int op_len,
363 const char *arg_ptr, int arg_len,
364 const char *val_ptr, int val_len,
365 char **out_ptr, int *out_len, int *out_size)
367 var_rc_t stat = VAR_ERR_UNDEFINED_OPERATION;
368 Dmsg0(100, "Enter operate_var\n");
373 if (op_len == 3 && strncmp(op_ptr, "inc", 3) == 0) {
375 if (val_len > (int)sizeof(buf) - 1) {
376 return VAR_ERR_OUT_OF_MEMORY;
378 memcpy(buf, arg_ptr, arg_len);
380 Dmsg1(100, "Arg=%s\n", buf);
381 memcpy(buf, val_ptr, val_len);
383 Dmsg1(100, "Val=%s\n", buf);
385 for (COUNTER *counter=NULL; (counter = (COUNTER *)GetNextRes(R_COUNTER, (RES *)counter)); ) {
386 if (strcmp(counter->name(), buf) == 0) {
387 Dmsg2(100, "counter=%s val=%s\n", counter->name(), buf);
400 * Expand an input line and return it.
402 * Returns: 0 on failure
403 * 1 on success and exp has expanded input
405 int variable_expansion(JCR *jcr, char *inp, POOLMEM **exp)
413 in_len = strlen(inp);
418 if ((stat = var_create(&var_ctx)) != VAR_OK) {
419 Jmsg(jcr, M_ERROR, 0, _("Cannot create var context: ERR=%s\n"), var_strerror(var_ctx, stat));
422 /* define callback */
423 if ((stat = var_config(var_ctx, VAR_CONFIG_CB_VALUE, lookup_var, (void *)jcr)) != VAR_OK) {
424 Jmsg(jcr, M_ERROR, 0, _("Cannot set var callback: ERR=%s\n"), var_strerror(var_ctx, stat));
428 /* define special operations */
429 if ((stat = var_config(var_ctx, VAR_CONFIG_CB_OPERATION, operate_var, (void *)jcr)) != VAR_OK) {
430 Jmsg(jcr, M_ERROR, 0, _("Cannot set var operate: ERR=%s\n"), var_strerror(var_ctx, stat));
434 /* unescape in place */
435 if ((stat = var_unescape(var_ctx, inp, in_len, inp, in_len+1, 0)) != VAR_OK) {
436 Jmsg(jcr, M_ERROR, 0, _("Cannot unescape string: ERR=%s\n"), var_strerror(var_ctx, stat));
440 in_len = strlen(inp);
442 /* expand variables */
443 if ((stat = var_expand(var_ctx, inp, in_len, &outp, &out_len, 0)) != VAR_OK) {
444 Jmsg(jcr, M_ERROR, 0, _("Cannot expand expression \"%s\": ERR=%s\n"),
445 inp, var_strerror(var_ctx, stat));
449 /* unescape once more in place */
450 if ((stat = var_unescape(var_ctx, outp, out_len, outp, out_len+1, 1)) != VAR_OK) {
451 Jmsg(jcr, M_ERROR, 0, _("Cannot unescape string: ERR=%s\n"), var_strerror(var_ctx, stat));
455 pm_strcpy(exp, outp);
460 /* destroy expansion context */
461 if ((stat = var_destroy(var_ctx)) != VAR_OK) {
462 Jmsg(jcr, M_ERROR, 0, _("Cannot destroy var context: ERR=%s\n"), var_strerror(var_ctx, stat));