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