]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/output.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / lib / output.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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
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
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
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
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
119 char *OutputWriter::end_group(bool append)
120 {
121    get_buf(append);
122    pm_strcat(buf, "\n");
123
124    return buf;
125 }
126
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
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  */
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  */
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
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_SIZE:
310       case OT_INT64:
311          i64 = va_arg(ap, int64_t);
312          Mmsg(tmp, "%s=%lld%c", k, i64, separator);
313          break;
314
315       case OT_PINT64:
316          u64 = va_arg(ap, uint64_t);
317          Mmsg(tmp, "%s=%llu%c", k, u64, separator);
318          break;
319
320       case OT_INT:
321          i64 = va_arg(ap, int);
322          Mmsg(tmp, "%s=%lld%c", k, i64, separator);
323          break;
324
325       case OT_JOBLEVEL:
326       case OT_JOBTYPE:
327       case OT_JOBSTATUS:
328          i32 = va_arg(ap, int32_t);
329          Mmsg(tmp, "%s=%c%c", k, (char) i32, separator);
330          break;
331
332       case OT_CLEAR:
333          **out = 0;
334          break;
335
336       case OT_END_OBJ:
337          pm_strcpy(tmp, "\n");
338          break;
339
340       case OT_START_OBJ:
341          i=0;
342          if (object_separator) {
343             for(; i < 32 ; i++) {
344                tmp[i] = object_separator;
345             }
346          }
347          tmp[i++] = '\n';
348          tmp[i] = 0;
349          break;
350
351       case OT_END:
352          /* wanted fallback */
353       default:
354          val = OT_END;
355       }
356
357       if (val != OT_END) {
358          pm_strcat(out, tmp);
359          val = (OutputType) va_arg(ap, int); /* OutputType is promoted to int when using ... */
360       }
361    }
362
363    free_pool_memory(tmp);
364    free_pool_memory(tmp2);
365    //Dmsg1(000, "%s", *out);
366    return *out;
367 }
368
369 #ifdef TEST_PROGRAM
370 int err=0;
371 int nb=0;
372 void _ok(const char *file, int l, const char *op, int value, const char *label)
373 {
374    nb++;
375    if (!value) {
376       err++;
377       printf("ERR %.30s %s:%i on %s\n", label, file, l, op);
378    } else {
379       printf("OK  %.30s\n", label);
380    }
381 }
382
383 #define ok(x, label) _ok(__FILE__, __LINE__, #x, (x), label)
384
385 void _nok(const char *file, int l, const char *op, int value, const char *label)
386 {
387    nb++;
388    if (value) {
389       err++;
390       printf("ERR %.30s %s:%i on !%s\n", label, file, l, op);
391    } else {
392       printf("OK  %.30s\n", label);
393    }
394 }
395
396 #define nok(x, label) _nok(__FILE__, __LINE__, #x, (x), label)
397
398 int report()
399 {
400    printf("Result %i/%i OK\n", nb - err, nb);
401    return err>0;
402 }
403
404 int main(int argc, char **argv)
405 {
406    char ed1[50];
407    OutputWriter wt;
408    POOLMEM *tmp = get_pool_memory(PM_FNAME);
409    *tmp = 0;
410
411    int         nb   = 10000;
412    const char *ptr  = "my value";
413    char       *str  = bstrdup("ptr");
414    int32_t     nb32 = -1;
415    int64_t     nb64 = -1;
416    btime_t     t    = time(NULL);
417
418    ok(strcmp(wt.get_options(ed1), "") == 0, "Default options");
419
420    Pmsg1(000, "%s", wt.start_group("test"));
421
422    wt.get_output(&tmp, OT_CLEAR,
423                  OT_STRING, "test", "my value",
424                  OT_STRING, "test2", ptr,
425                  OT_STRING, "test3", str,
426                  OT_INT,    "nb",   nb,
427                  OT_INT32,  "nb32", nb32,
428                  OT_INT64,  "nb64", nb64,
429                  OT_BTIME,  "now",  t,
430                  OT_END);
431
432    Pmsg1(000, "%s", tmp);
433
434    free_pool_memory(tmp);
435
436
437    Pmsg1(000, "%s",
438          wt.get_output(OT_CLEAR,
439                  OT_START_OBJ,
440                  OT_STRING, "test", "my value",
441                  OT_STRING, "test2", ptr,
442                  OT_STRING, "test3", str,
443                  OT_INT,    "nb",   nb,
444                  OT_INT32,  "nb32", nb32,
445                  OT_INT64,  "nb64", nb64,
446                  OT_BTIME,  "now",  t,
447                  OT_END_OBJ,
448                  OT_END));
449
450    wt.set_time_format(OTT_TIME_UNIX);
451    ok(strcmp("t1", wt.get_options(ed1)) == 0, "Check unix time format");
452
453    Pmsg1(000, "%s",
454          wt.get_output(OT_CLEAR,
455                  OT_BTIME,  "now",  t,
456                  OT_END));
457
458    wt.set_time_format(OTT_TIME_NC);
459    ok(strcmp("t2", wt.get_options(ed1)) == 0, "Check NC time format");
460
461    Pmsg1(000, "%s",
462          wt.get_output(OT_CLEAR,
463                  OT_BTIME,  "now",  t,
464                  OT_END));
465
466    Pmsg1(000, "%s", wt.end_group(false));
467
468    wt.parse_options("s43t1O");
469    ok(strcmp(wt.get_options(ed1), "s43t1") == 0, "Check options after parsing");
470
471    ok(strstr(
472          wt.get_output(OT_CLEAR,
473                  OT_BTIME,  "now",  t,
474                  OT_STRING, "brazil", "test",
475                  OT_END),
476          "+brazil=test+") != NULL,
477       "Check separator");
478
479    wt.parse_options("CS35");
480    ok(strcmp(wt.get_options(ed1), "S35") == 0, "Check options after parsing");
481
482    Pmsg1(000, "%s",
483          wt.get_output(OT_CLEAR,
484                  OT_START_OBJ,
485                  OT_STRING, "test", "my value",
486                  OT_STRING, "test2", ptr,
487                  OT_END_OBJ,
488                  OT_START_OBJ,
489                  OT_STRING, "test", "my value",
490                  OT_STRING, "test2", ptr,
491                  OT_END_OBJ,
492                  OT_END));
493
494    return report();
495 }
496 #endif