1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * Written by: Eric Bollengier, December MMXIII
21 */
22
23 #define OUTPUT_C /* control dll export in output.h */
24 #include "output.h"
25 #include "plugins.h"
26
27 /* use new output (lowercase, no special char) */
28 #define OF_USE_NEW_OUTPUT 1
29
parse_options(const char * options)30 void OutputWriter::parse_options(const char *options)
31 {
32 int nb=0;
33 const char *p = options;
34 while (*p) {
35 nb=0;
36
37 switch(*p) {
38 case 'C':
39 flags = 0;
40 set_time_format(OW_DEFAULT_TIMEFORMAT);
41 set_separator(OW_DEFAULT_SEPARATOR);
42 break;
43
44 case 'S': /* object separator */
45 while(isdigit(*(p+1))) {
46 nb = nb*10 + (*(++p) - '0');
47 }
48 if (isascii(nb)) {
49 set_object_separator((char) nb);
50 }
51 break;
52
53 case 'o':
54 flags |= OF_USE_NEW_OUTPUT; /* lowercase and only isalpha */
55 break;
56
57 case 't': /* Time format */
58 if (isdigit(*(p+1))) {
59 nb = (*(++p) - '0');
60 set_time_format((OutputTimeType) nb);
61 }
62 break;
63
64 case 's': /* Separator */
65 while(isdigit(*(p+1))) {
66 nb = nb*10 + (*(++p) - '0');
67 }
68 if (isascii(nb)) {
69 set_separator((char) nb);
70 }
71 break;
72 default:
73 break;
74 }
75 p++;
76 }
77 }
78
get_options(char * dest)79 char *OutputWriter::get_options(char *dest)
80 {
81 char ed1[50];
82 *dest = *ed1 = 0;
83 if (separator != OW_DEFAULT_SEPARATOR) {
84 snprintf(dest, 50, "s%d", (int)separator);
85 }
86 if (object_separator) {
87 snprintf(ed1, sizeof(ed1), "S%d", (int) object_separator);
88 bstrncat(dest, ed1, sizeof(ed1));
89 }
90 if (timeformat != OW_DEFAULT_TIMEFORMAT) {
91 snprintf(ed1, sizeof(ed1), "t%d", (int) timeformat);
92 bstrncat(dest, ed1, sizeof(ed1));
93 }
94 if (flags & OF_USE_NEW_OUTPUT) {
95 bstrncat(dest, "o", 1);
96 }
97 return dest;
98 }
99
get_buf(bool append)100 void OutputWriter::get_buf(bool append)
101 {
102 if (!buf) {
103 buf = get_pool_memory(PM_MESSAGE);
104 *buf = 0;
105
106 } else if (!append) {
107 *buf = 0;
108 }
109 }
110
start_group(const char * name,bool append)111 char *OutputWriter::start_group(const char *name, bool append)
112 {
113 get_buf(append);
114 pm_strcat(buf, name);
115 pm_strcat(buf, ":\n");
116 return buf;
117 }
118
end_group(bool append)119 char *OutputWriter::end_group(bool append)
120 {
121 get_buf(append);
122 pm_strcat(buf, "\n");
123
124 return buf;
125 }
126
start_list(const char * name,bool append)127 char *OutputWriter::start_list(const char *name, bool append)
128 {
129 get_buf(append);
130 pm_strcat(buf, name);
131 pm_strcat(buf, ": [\n");
132 return buf;
133 }
134
end_list(bool append)135 char *OutputWriter::end_list(bool append)
136 {
137 get_buf(append);
138 pm_strcat(buf, "]\n");
139
140 return buf;
141 }
142
143 /* Usage:
144 * get_output(
145 * OT_STRING, "name", "value",
146 * OT_PINT32, "age", 10,
147 * OT_TIME, "birth-date", 1120202002,
148 * OT_PINT64, "weight", 100,
149 * OT_END);
150 *
151 *
152 * "name=value\nage=10\nbirt-date=2012-01-12 10:20:00\nweight=100\n"
153 *
154 */
get_output(OutputType first,...)155 char *OutputWriter::get_output(OutputType first, ...)
156 {
157 char *ret;
158 va_list arg_ptr;
159
160 get_buf(true); /* Append to the current string */
161
162 va_start(arg_ptr, first);
163 ret = get_output(arg_ptr, &buf, first);
164 va_end(arg_ptr);
165
166 return ret;
167 }
168
169 /* Usage:
170 * get_output(&out,
171 * OT_STRING, "name", "value",
172 * OT_PINT32, "age", 10,
173 * OT_TIME, "birth-date", 1120202002,
174 * OT_PINT64, "weight", 100,
175 * OT_END);
176 *
177 *
178 * "name=value\nage=10\nbirt-date=2012-01-12 10:20:00\nweight=100\n"
179 *
180 */
get_output(POOLMEM ** out,OutputType first,...)181 char *OutputWriter::get_output(POOLMEM **out, OutputType first, ...)
182 {
183 va_list arg_ptr;
184 char *ret;
185
186 va_start(arg_ptr, first);
187 ret = get_output(arg_ptr, out, first);
188 va_end(arg_ptr);
189
190 return ret;
191 }
192
get_output(va_list ap,POOLMEM ** out,OutputType first)193 char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
194 {
195 char ed1[MAX_TIME_LENGTH];
196 int i;
197 int64_t i64;
198 uint64_t u64;
199 int32_t i32;
200 double d;
201 btime_t bt;
202 char *s = NULL, *k = NULL;
203 alist *lst;
204 Plugin *plug;
205 POOLMEM *tmp2 = get_pool_memory(PM_FNAME);
206 POOLMEM *tmp = get_pool_memory(PM_FNAME);
207 OutputType val = first;
208
209 while (val != OT_END) {
210
211 *tmp = 0;
212
213 /* Some arguments are not using a keyword */
214 switch (val) {
215 case OT_END:
216 case OT_START_OBJ:
217 case OT_END_OBJ:
218 case OT_CLEAR:
219 break;
220
221 default:
222 k = va_arg(ap, char *); /* Get the variable name */
223
224 /* If requested, we can put the keyword in lowercase */
225 if (flags & OF_USE_NEW_OUTPUT) {
226 tmp2 = check_pool_memory_size(tmp2, strlen(k)+1);
227 for (i = 0; k[i] ; i++) {
228 if (isalnum(k[i])) {
229 tmp2[i] = tolower(k[i]);
230 } else {
231 tmp2[i] = '_';
232 }
233 }
234 tmp2[i] = 0;
235 k = tmp2;
236 }
237 }
238
239 //Dmsg2(000, "%d - %s\n", val, k);
240
241 switch (val) {
242 case OT_ALIST_STR:
243 lst = va_arg(ap, alist *);
244 i = 0;
245 Mmsg(tmp, "%s=", k);
246 if (lst) {
247 foreach_alist(s, lst) {
248 if (i++ > 0) {
249 pm_strcat(tmp, ",");
250 }
251 pm_strcat(tmp, s);
252 }
253 }
254 pm_strcat(tmp, separator_str);
255 break;
256 case OT_PLUGINS:
257 lst = va_arg(ap, alist *);
258 i = 0;
259 pm_strcpy(tmp, "plugins=");
260 if (lst) {
261 foreach_alist(plug, lst) {
262 if (i++ > 0) {
263 pm_strcat(tmp, ",");
264 }
265 pm_strcat(tmp, plug->file);
266 }
267 }
268 pm_strcat(tmp, separator_str);
269 break;
270 case OT_RATIO:
271 d = va_arg(ap, double);
272 Mmsg(tmp, "%s=%.2f%c", k, d, separator);
273 break;
274
275 case OT_STRING:
276 s = va_arg(ap, char *);
277 Mmsg(tmp, "%s=%s%c", k, NPRTB(s), separator) ;
278 break;
279
280 case OT_INT32:
281 i32 = va_arg(ap, int32_t);
282 Mmsg(tmp, "%s=%d%c", k, i32, separator);
283 break;
284
285 case OT_UTIME:
286 case OT_BTIME:
287 if (val == OT_UTIME) {
288 bt = va_arg(ap, utime_t);
289 } else {
290 bt = va_arg(ap, btime_t);
291 }
292 switch (timeformat) {
293 case OTT_TIME_NC: /* Formatted time for user display: dd-Mon hh:mm */
294 bstrftime_ny(ed1, sizeof(ed1), bt);
295 break;
296
297 case OTT_TIME_UNIX: /* unix timestamp */
298 bsnprintf(ed1, sizeof(ed1), "%lld", bt);
299 break;
300
301 case OTT_TIME_ISO:
302 /* wanted fallback */
303 default:
304 bstrutime(ed1, sizeof(ed1), bt);
305 }
306 Mmsg(tmp, "%s_epoch=%lld%c%s=%s%c", k, bt, separator, k, ed1, separator);
307 break;
308
309 case OT_DURATION:
310 bt = va_arg(ap, utime_t);
311 bstrutime(ed1, sizeof(ed1), bt);
312 Mmsg(tmp, "%s=%lld%c%s_str=%s%c", k, bt, separator, k, edit_utime(bt, ed1, sizeof(ed1)), separator);
313 break;
314
315 case OT_SIZE:
316 case OT_INT64:
317 i64 = va_arg(ap, int64_t);
318 Mmsg(tmp, "%s=%lld%c", k, i64, separator);
319 break;
320
321 case OT_PINT64:
322 u64 = va_arg(ap, uint64_t);
323 Mmsg(tmp, "%s=%llu%c", k, u64, separator);
324 break;
325
326 case OT_INT:
327 i64 = va_arg(ap, int);
328 Mmsg(tmp, "%s=%lld%c", k, i64, separator);
329 break;
330
331 case OT_JOBLEVEL:
332 case OT_JOBTYPE:
333 case OT_JOBSTATUS:
334 i32 = va_arg(ap, int32_t);
335 if (i32 == 0) {
336 Mmsg(tmp, "%s=%c", k, separator);
337 } else {
338 Mmsg(tmp, "%s=%c%c", k, (char)i32, separator);
339 }
340 break;
341
342 case OT_CLEAR:
343 **out = 0;
344 break;
345
346 case OT_END_OBJ:
347 pm_strcpy(tmp, "\n");
348 break;
349
350 case OT_START_OBJ:
351 i=0;
352 if (object_separator) {
353 for(; i < 32 ; i++) {
354 tmp[i] = object_separator;
355 }
356 }
357 tmp[i++] = '\n';
358 tmp[i] = 0;
359 break;
360
361 case OT_END:
362 /* wanted fallback */
363 default:
364 val = OT_END;
365 }
366
367 if (val != OT_END) {
368 pm_strcat(out, tmp);
369 val = (OutputType) va_arg(ap, int); /* OutputType is promoted to int when using ... */
370 }
371 }
372
373 free_pool_memory(tmp);
374 free_pool_memory(tmp2);
375 //Dmsg1(000, "%s", *out);
376 return *out;
377 }
378
379 #ifndef TEST_PROGRAM
380 #define TEST_PROGRAM_A
381 #endif
382
383 #ifdef TEST_PROGRAM
384 #include "unittests.h"
385
main(int argc,char ** argv)386 int main(int argc, char **argv)
387 {
388 Unittests output_test("output_test");
389 char ed1[50];
390 OutputWriter wt;
391 POOLMEM *tmp = get_pool_memory(PM_FNAME);
392 *tmp = 0;
393
394 int nb = 10000;
395 const char *ptr = "my value";
396 char *str = bstrdup("ptr");
397 int32_t nb32 = -1;
398 int64_t nb64 = -1;
399 btime_t t = time(NULL);
400
401 ok(strcmp(wt.get_options(ed1), "") == 0, "Default options");
402
403 Pmsg1(000, "%s", wt.start_group("test"));
404
405 wt.get_output(&tmp, OT_CLEAR,
406 OT_STRING, "test", "my value",
407 OT_STRING, "test2", ptr,
408 OT_STRING, "test3", str,
409 OT_INT, "nb", nb,
410 OT_INT32, "nb32", nb32,
411 OT_INT64, "nb64", nb64,
412 OT_BTIME, "now", t,
413 OT_END);
414
415 Pmsg1(000, "%s", tmp);
416
417 free_pool_memory(tmp);
418
419 Pmsg1(000, "%s",
420 wt.get_output(OT_CLEAR,
421 OT_START_OBJ,
422 OT_STRING, "test", "my value",
423 OT_STRING, "test2", ptr,
424 OT_STRING, "test3", str,
425 OT_INT, "nb", nb,
426 OT_INT32, "nb32", nb32,
427 OT_INT64, "nb64", nb64,
428 OT_BTIME, "now", t,
429 OT_END_OBJ,
430 OT_END));
431
432 wt.set_time_format(OTT_TIME_UNIX);
433 ok(strcmp("t1", wt.get_options(ed1)) == 0, "Check unix time format");
434
435 Pmsg1(000, "%s",
436 wt.get_output(OT_CLEAR,
437 OT_BTIME, "now", t,
438 OT_END));
439
440 wt.set_time_format(OTT_TIME_NC);
441 ok(strcmp("t2", wt.get_options(ed1)) == 0, "Check NC time format");
442
443 Pmsg1(000, "%s",
444 wt.get_output(OT_CLEAR,
445 OT_BTIME, "now", t,
446 OT_END));
447
448 Pmsg1(000, "%s", wt.end_group(false));
449
450 wt.parse_options("s43t1O");
451 ok(strcmp(wt.get_options(ed1), "s43t1") == 0, "Check options after parsing");
452
453 ok(strstr(
454 wt.get_output(OT_CLEAR,
455 OT_BTIME, "now", t,
456 OT_STRING, "brazil", "test",
457 OT_END),
458 "+brazil=test+") != NULL,
459 "Check separator");
460
461 wt.parse_options("CS35");
462 ok(strcmp(wt.get_options(ed1), "S35") == 0, "Check options after parsing");
463
464 Pmsg1(000, "%s",
465 wt.get_output(OT_CLEAR,
466 OT_START_OBJ,
467 OT_STRING, "test", "my value",
468 OT_STRING, "test2", ptr,
469 OT_END_OBJ,
470 OT_START_OBJ,
471 OT_STRING, "test", "my value",
472 OT_STRING, "test2", ptr,
473 OT_END_OBJ,
474 OT_END));
475
476 free(str);
477
478 return report();
479 }
480 #endif /* TEST_PROGRAM */
481