]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/scan.c
Backport from Bacula Enterprise
[bacula/bacula] / bacula / src / lib / scan.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  *   scan.c -- scanning routines for Bacula
22  *
23  *    Kern Sibbald, MM  separated from util.c MMIII
24  *
25  */
26
27
28 #include "bacula.h"
29 #include "jcr.h"
30 #include "findlib/find.h"
31
32 /* Strip leading space from command line arguments */
33 void strip_leading_space(char *str)
34 {
35    char *p = str;
36    while (B_ISSPACE(*p)) {
37       p++;
38    }
39    if (p != str) {
40       strcpy(str, p);
41    }
42 }
43
44
45 /* Strip any trailing junk from the command */
46 void strip_trailing_junk(char *cmd)
47 {
48    char *p;
49
50    /* strip trailing junk from command */
51    p = cmd - 1 + strlen(cmd);
52    while ((p >= cmd) && (B_ISSPACE(*p) || *p == '\n' || *p == '\r')) {
53       *p-- = 0;
54    } 
55 }
56
57 /* Strip any trailing newline characters from the string */
58 void strip_trailing_newline(char *cmd)
59 {
60    char *p;
61    p = cmd - 1 + strlen(cmd);
62    while ((p >= cmd) && (*p == '\n' || *p == '\r')) *p-- = 0;
63 }
64
65 /* Strip any trailing slashes from a directory path */
66 void strip_trailing_slashes(char *dir)
67 {
68    char *p;
69
70    /* strip trailing slashes */
71    p = dir -1 + strlen(dir);
72    while (p >= dir && IsPathSeparator(*p)) *p-- = 0;
73 }
74
75 /*
76  * Skip spaces
77  *  Returns: 0 on failure (EOF)
78  *           1 on success
79  *           new address in passed parameter
80  */
81 bool skip_spaces(char **msg)
82 {
83    char *p = *msg;
84    if (!p) {
85       return false;
86    }
87    while (*p && B_ISSPACE(*p)) {
88       p++;
89    }
90    *msg = p;
91    return *p ? true : false;
92 }
93
94 /*
95  * Skip nonspaces
96  *  Returns: 0 on failure (EOF)
97  *           1 on success
98  *           new address in passed parameter
99  */
100 bool skip_nonspaces(char **msg)
101 {
102    char *p = *msg;
103
104    if (!p) {
105       return false;
106    }
107    while (*p && !B_ISSPACE(*p)) {
108       p++;
109    }
110    *msg = p;
111    return *p ? true : false;
112 }
113
114 /* folded search for string - case insensitive */
115 int
116 fstrsch(const char *a, const char *b)   /* folded case search */
117 {
118    const char *s1,*s2;
119    char c1, c2;
120
121    s1=a;
122    s2=b;
123    while (*s1) {                      /* do it the fast way */
124       if ((*s1++ | 0x20) != (*s2++ | 0x20))
125          return 0;                    /* failed */
126    }
127    while (*a) {                       /* do it over the correct slow way */
128       if (B_ISUPPER(c1 = *a)) {
129          c1 = tolower((int)c1);
130       }
131       if (B_ISUPPER(c2 = *b)) {
132          c2 = tolower((int)c2);
133       }
134       if (c1 != c2) {
135          return 0;
136       }
137       a++;
138       b++;
139    }
140    return 1;
141 }
142
143
144 /*
145  * Return next argument from command line.  Note, this
146  *   routine is destructive because it stored 0 at the end
147  *   of each argument.
148  * Called with pointer to pointer to command line. This
149  *   pointer is updated to point to the remainder of the
150  *   command line.
151  *
152  * Returns pointer to next argument -- don't store the result
153  *   in the pointer you passed as an argument ...
154  *   The next argument is terminated by a space unless within
155  *   quotes. Double quote characters (unless preceded by a \) are
156  *   stripped.
157  *
158  */
159 char *next_arg(char **s)
160 {
161    char *p, *q, *n;
162    bool in_quote = false;
163
164    /* skip past spaces to next arg */
165    for (p=*s; *p && B_ISSPACE(*p); ) {
166       p++;
167    }
168    Dmsg1(900, "Next arg=%s\n", p);
169    for (n = q = p; *p ; ) {
170       if (*p == '\\') {                 /* slash? */
171          p++;                           /* yes, skip it */
172          if (*p) {
173             *q++ = *p++;
174          } else {
175             *q++ = *p;
176          }
177          continue;
178       }
179       if (*p == '"') {                  /* start or end of quote */
180          p++;
181          in_quote = !in_quote;          /* change state */
182          continue;
183       }
184       if (!in_quote && B_ISSPACE(*p)) { /* end of field */
185          p++;
186          break;
187       }
188       *q++ = *p++;
189    }
190    *q = 0;
191    *s = p;
192    Dmsg2(900, "End arg=%s next=%s\n", n, p);
193    return n;
194 }
195
196 /*
197  * This routine parses the input command line.
198  * It makes a copy in args, then builds an
199  *  argc, argk, argv list where:
200  *
201  *  argc = count of arguments
202  *  argk[i] = argument keyword (part preceding =)
203  *  argv[i] = argument value (part after =)
204  *
205  *  example:  arg1 arg2=abc arg3=
206  *
207  *  argc = c
208  *  argk[0] = arg1
209  *  argv[0] = NULL
210  *  argk[1] = arg2
211  *  argv[1] = abc
212  *  argk[2] = arg3
213  *  argv[2] =
214  */
215 int parse_args(POOLMEM *cmd, POOLMEM **args, int *argc,
216                char **argk, char **argv, int max_args)
217 {
218    char *p;
219
220    parse_args_only(cmd, args, argc, argk, argv, max_args);
221
222    /* Separate keyword and value */
223    for (int i=0; i < *argc; i++) {
224       p = strchr(argk[i], '=');
225       if (p) {
226          *p++ = 0;                    /* terminate keyword and point to value */
227       }
228       argv[i] = p;                    /* save ptr to value or NULL */
229    }
230 #ifdef xxx_debug
231    for (int i=0; i < *argc; i++) {
232       Pmsg3(000, "Arg %d: kw=%s val=%s\n", i, argk[i], argv[i]?argv[i]:"NULL");
233    }
234 #endif
235    return 1;
236 }
237
238
239 /*
240  * This routine parses the input command line.
241  *   It makes a copy in args, then builds an
242  *   argc, argk, but no argv (values).
243  *   This routine is useful for scanning command lines where the data
244  *   is a filename and no keywords are expected.  If we scan a filename
245  *   for keywords, any = in the filename will be interpreted as the
246  *   end of a keyword, and this is not good.
247  *
248  *  argc = count of arguments
249  *  argk[i] = argument keyword (part preceding =)
250  *  argv[i] = NULL
251  *
252  *  example:  arg1 arg2=abc arg3=
253  *
254  *  argc = c
255  *  argk[0] = arg1
256  *  argv[0] = NULL
257  *  argk[1] = arg2=abc
258  *  argv[1] = NULL
259  *  argk[2] = arg3
260  *  argv[2] =
261  */
262 int parse_args_only(POOLMEM *cmd, POOLMEM **args, int *argc,
263                     char **argk, char **argv, int max_args)
264 {
265    char *p, *n;
266
267    pm_strcpy(args, cmd);
268    strip_trailing_junk(*args);
269    p = *args;
270    *argc = 0;
271    /* Pick up all arguments */
272    while (*argc < max_args) {
273       n = next_arg(&p);
274       if (*n) {
275          argk[*argc] = n;
276          argv[(*argc)++] = NULL;
277       } else {
278          break;
279       }
280    }
281    return 1;
282 }
283
284
285 /*
286  * Given a full filename, split it into its path
287  *  and filename parts. They are returned in pool memory
288  *  in the arguments provided.
289  */
290 void split_path_and_filename(const char *fname, POOLMEM **path, int *pnl,
291         POOLMEM **file, int *fnl)
292 {
293    const char *f;
294    int slen;
295    int len = slen = strlen(fname);
296
297    /*
298     * Find path without the filename.
299     * I.e. everything after the last / is a "filename".
300     * OK, maybe it is a directory name, but we treat it like
301     * a filename. If we don't find a / then the whole name
302     * must be a path name (e.g. c:).
303     */
304    f = fname + len - 1;
305    /* "strip" any trailing slashes */
306    while (slen > 1 && IsPathSeparator(*f)) {
307       slen--;
308       f--;
309    }
310    /* Walk back to last slash -- begin of filename */
311    while (slen > 0 && !IsPathSeparator(*f)) {
312       slen--;
313       f--;
314    }
315    if (IsPathSeparator(*f)) {         /* did we find a slash? */
316       f++;                            /* yes, point to filename */
317    } else {                           /* no, whole thing must be path name */
318       f = fname;
319    }
320    Dmsg2(200, "after strip len=%d f=%s\n", len, f);
321    *fnl = fname - f + len;
322    if (*fnl > 0) {
323       *file = check_pool_memory_size(*file, *fnl+1);
324       memcpy(*file, f, *fnl);    /* copy filename */
325    }
326    (*file)[*fnl] = 0;
327
328    *pnl = f - fname;
329    if (*pnl > 0) {
330       *path = check_pool_memory_size(*path, *pnl+1);
331       memcpy(*path, fname, *pnl);
332    }
333    (*path)[*pnl] = 0;
334
335    Dmsg2(200, "pnl=%d fnl=%d\n", *pnl, *fnl);
336    Dmsg3(200, "split fname=%s path=%s file=%s\n", fname, *path, *file);
337 }
338
339 /*
340  * Extremely simple sscanf. Handles only %(u,d,ld,qd,qu,lu,lld,llu,c,nns)
341  *
342  * Note, BIG is the default maximum length when no length
343  *   has been specified for %s. If it is not big enough, then
344  *   simply add a length such as %10000s.
345  */
346 const int BIG = 1000;
347 int bsscanf(const char *buf, const char *fmt, ...)
348 {
349    va_list ap;
350    int count = 0;
351    void *vp;
352    char *cp;
353    int l = 0;
354    int max_len = BIG;
355    uint64_t value;
356    bool error = false;
357    bool negative;
358
359    va_start(ap, fmt);
360    while (*fmt && !error) {
361 //    Dmsg1(000, "fmt=%c\n", *fmt);
362       if (*fmt == '%') {
363          fmt++;
364 //       Dmsg1(000, "Got %% nxt=%c\n", *fmt);
365 switch_top:
366          switch (*fmt++) {
367          case 'u':
368             value = 0;
369             while (B_ISDIGIT(*buf)) {
370                value = B_TIMES10(value) + *buf++ - '0';
371             }
372             vp = (void *)va_arg(ap, void *);
373 //          Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
374             if (l == 0) {
375                *((int *)vp) = (int)value;
376             } else if (l == 1) {
377                *((uint32_t *)vp) = (uint32_t)value;
378 //             Dmsg0(000, "Store 32 bit int\n");
379             } else {
380                *((uint64_t *)vp) = (uint64_t)value;
381 //             Dmsg0(000, "Store 64 bit int\n");
382             }
383             count++;
384             l = 0;
385             break;
386          case 'd':
387             value = 0;
388             if (*buf == '-') {
389                negative = true;
390                buf++;
391             } else {
392                negative = false;
393             }
394             while (B_ISDIGIT(*buf)) {
395                value = B_TIMES10(value) + *buf++ - '0';
396             }
397             if (negative) {
398                value = -value;
399             }
400             vp = (void *)va_arg(ap, void *);
401 //          Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
402             if (l == 0) {
403                *((int *)vp) = (int)value;
404             } else if (l == 1) {
405                *((int32_t *)vp) = (int32_t)value;
406 //             Dmsg0(000, "Store 32 bit int\n");
407             } else {
408                *((int64_t *)vp) = (int64_t)value;
409 //             Dmsg0(000, "Store 64 bit int\n");
410             }
411             count++;
412             l = 0;
413             break;
414          case 'l':
415 //          Dmsg0(000, "got l\n");
416             l = 1;
417             if (*fmt == 'l') {
418                l++;
419                fmt++;
420             }
421             if (*fmt == 'd' || *fmt == 'u') {
422                goto switch_top;
423             }
424 //          Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
425             error = true;
426             break;
427          case 'q':
428             l = 2;
429             if (*fmt == 'd' || *fmt == 'u') {
430                goto switch_top;
431             }
432 //          Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
433             error = true;
434             break;
435          case 's':
436 //          Dmsg1(000, "Store string max_len=%d\n", max_len);
437             cp = (char *)va_arg(ap, char *);
438             while (*buf && !B_ISSPACE(*buf) && max_len-- > 0) {
439                *cp++ = *buf++;
440             }
441             *cp = 0;
442             count++;
443             max_len = BIG;
444             break;
445          case 'c':
446             cp = (char *)va_arg(ap, char *);
447             *cp = *buf++;
448             count++;
449             break;
450          case '%':
451             if (*buf++ != '%') {
452                error = true;
453             }
454             break;
455          default:
456             fmt--;
457             max_len = 0;
458             while (B_ISDIGIT(*fmt)) {
459                max_len = B_TIMES10(max_len) + *fmt++ - '0';
460             }
461 //          Dmsg1(000, "Default max_len=%d\n", max_len);
462             if (*fmt == 's') {
463                goto switch_top;
464             }
465 //          Dmsg1(000, "Default c=%c\n", *fmt);
466             error = true;
467             break;                    /* error: unknown format */
468          }
469          continue;
470
471       /* White space eats zero or more whitespace */
472       } else if (B_ISSPACE(*fmt)) {
473          fmt++;
474          while (B_ISSPACE(*buf)) {
475             buf++;
476          }
477       /* Plain text must match */
478       } else if (*buf++ != *fmt++) {
479 //       Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
480          error = true;
481          break;
482       }
483    }
484    va_end(ap);
485 // Dmsg2(000, "Error=%d count=%d\n", error, count);
486    if (error) {
487       count = -1;
488    }
489    return count;
490 }
491
492 #ifdef TEST_PROGRAM
493 int main(int argc, char *argv[])
494 {
495    char buf[100];
496    uint32_t val32;
497    uint64_t val64;
498    uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
499    char Job[200];
500    int cnt;
501    char *helloreq= "Hello *UserAgent* calling\n";
502    char *hello = "Hello %127s calling\n";
503    char *catreq =
504 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
505 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
506   "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
507   "StartBlock=%u EndBlock=%u\n";
508 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
509    " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
510    " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
511    " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
512    " VolReadTime=%" lld " VolWriteTime=%" lld;
513    char *media =
514 "1000 OK VolName=TestVolume001 VolJobs=0 VolFiles=0 VolBlocks=0 VolBytes=1 VolMounts=0 VolErrors=0 VolWrites=0 MaxVolBytes=0 VolCapacityBytes=0 VolStatus=Append Slot=0 MaxVolJobs=0 MaxVolFiles=0 InChanger=1 VolReadTime=0 VolWriteTime=0";
515 struct VOLUME_CAT_INFO {
516    /* Media info for the current Volume */
517    uint32_t VolCatJobs;               /* number of jobs on this Volume */
518    uint32_t VolCatFiles;              /* Number of files */
519    uint32_t VolCatBlocks;             /* Number of blocks */
520    uint64_t VolCatBytes;              /* Number of bytes written */
521    uint32_t VolCatMounts;             /* Number of mounts this volume */
522    uint32_t VolCatErrors;             /* Number of errors this volume */
523    uint32_t VolCatWrites;             /* Number of writes this volume */
524    uint32_t VolCatReads;              /* Number of reads this volume */
525    uint64_t VolCatRBytes;             /* Number of bytes read */
526    uint32_t VolCatRecycles;           /* Number of recycles this volume */
527    int32_t  Slot;                     /* Slot in changer */
528    bool     InChanger;                /* Set if vol in current magazine */
529    uint32_t VolCatMaxJobs;            /* Maximum Jobs to write to volume */
530    uint32_t VolCatMaxFiles;           /* Maximum files to write to volume */
531    uint64_t VolCatMaxBytes;           /* Max bytes to write to volume */
532    uint64_t VolCatCapacityBytes;      /* capacity estimate */
533    uint64_t VolReadTime;              /* time spent reading */
534    uint64_t VolWriteTime;             /* time spent writing this Volume */
535    char VolCatStatus[20];             /* Volume status */
536    char VolCatName[MAX_NAME_LENGTH];  /* Desired volume to mount */
537 };
538    struct VOLUME_CAT_INFO vol;
539
540 #ifdef xxx
541    bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
542    printf("%s %d %lld\n", buf, val32, val64);
543
544    *Job=0;
545    cnt = bsscanf(catreq, Create_job_media, &Job,
546       &FirstIndex, &LastIndex, &StartFile, &EndFile,
547       &StartBlock, &EndBlock);
548    printf("cnt=%d Job=%s\n", cnt, Job);
549    cnt = bsscanf(helloreq, hello, &Job);
550    printf("cnt=%d Agent=%s\n", cnt, Job);
551 #endif
552    cnt = bsscanf(media, OK_media,
553                vol.VolCatName,
554                &vol.VolCatJobs, &vol.VolCatFiles,
555                &vol.VolCatBlocks, &vol.VolCatBytes,
556                &vol.VolCatMounts, &vol.VolCatErrors,
557                &vol.VolCatWrites, &vol.VolCatMaxBytes,
558                &vol.VolCatCapacityBytes, vol.VolCatStatus,
559                &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
560                &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
561    printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);
562
563 }
564
565 #endif