]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/scan.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / lib / scan.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2018 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 /*
496  * Return next name from a comma separated list.  Note, this
497  *   routine is destructive because it stored 0 at the end
498  *   of each argument.
499  * Called with pointer to pointer to command line. This
500  *   pointer is updated to point to the remainder of the
501  *   command line.
502  *
503  * Returns pointer to next name -- don't store the result
504  *   in the pointer you passed as an argument ...
505  *   The next argument is terminated by a , unless within
506  *   quotes. Double quote characters (unless preceded by a \) are
507  *   stripped.
508  *
509  */
510 char *next_name(char **s)
511 {
512    char *p, *q, *n;
513    bool in_quote = false;
514
515    if (s == NULL || *s == NULL || **s == '\0') {
516       return NULL;
517    }
518    p = *s;
519    Dmsg1(900, "Next name=%s\n", p);
520    for (n = q = p; *p ; ) {
521       if (*p == '\\') {                 /* slash? */
522          p++;                           /* yes, skip it */
523          if (*p) {
524             *q++ = *p++;
525          } else {
526             *q++ = *p;
527          }
528          continue;
529       }
530       if (*p == '"') {                  /* start or end of quote */
531          p++;
532          in_quote = !in_quote;          /* change state */
533          continue;
534       }
535       if (!in_quote && *p == ',') { /* end of field */
536          p++;
537          break;
538       }
539       *q++ = *p++;
540    }
541    *q = 0;
542    *s = p;
543    Dmsg2(900, "End arg=%s next=%s\n", n, p);
544    return n;
545 }
546
547 #ifdef TEST_PROGRAM
548 int main(int argc, char *argv[])
549 {
550    char buf[100];
551    uint32_t val32;
552    uint64_t val64;
553    uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
554    char Job[200];
555    int cnt;
556    char *helloreq= "Hello *UserAgent* calling\n";
557    char *hello = "Hello %127s calling\n";
558    char *catreq =
559 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
560 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
561   "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
562   "StartBlock=%u EndBlock=%u\n";
563 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
564    " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
565    " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
566    " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
567    " VolReadTime=%" lld " VolWriteTime=%" lld;
568    char *media =
569 "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";
570 struct VOLUME_CAT_INFO {
571    /* Media info for the current Volume */
572    uint32_t VolCatJobs;               /* number of jobs on this Volume */
573    uint32_t VolCatFiles;              /* Number of files */
574    uint32_t VolCatBlocks;             /* Number of blocks */
575    uint64_t VolCatBytes;              /* Number of bytes written */
576    uint32_t VolCatMounts;             /* Number of mounts this volume */
577    uint32_t VolCatErrors;             /* Number of errors this volume */
578    uint32_t VolCatWrites;             /* Number of writes this volume */
579    uint32_t VolCatReads;              /* Number of reads this volume */
580    uint64_t VolCatRBytes;             /* Number of bytes read */
581    uint32_t VolCatRecycles;           /* Number of recycles this volume */
582    int32_t  Slot;                     /* Slot in changer */
583    bool     InChanger;                /* Set if vol in current magazine */
584    uint32_t VolCatMaxJobs;            /* Maximum Jobs to write to volume */
585    uint32_t VolCatMaxFiles;           /* Maximum files to write to volume */
586    uint64_t VolCatMaxBytes;           /* Max bytes to write to volume */
587    uint64_t VolCatCapacityBytes;      /* capacity estimate */
588    uint64_t VolReadTime;              /* time spent reading */
589    uint64_t VolWriteTime;             /* time spent writing this Volume */
590    char VolCatStatus[20];             /* Volume status */
591    char VolCatName[MAX_NAME_LENGTH];  /* Desired volume to mount */
592 };
593    struct VOLUME_CAT_INFO vol;
594
595 #ifdef xxx
596    bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
597    printf("%s %d %lld\n", buf, val32, val64);
598
599    *Job=0;
600    cnt = bsscanf(catreq, Create_job_media, &Job,
601       &FirstIndex, &LastIndex, &StartFile, &EndFile,
602       &StartBlock, &EndBlock);
603    printf("cnt=%d Job=%s\n", cnt, Job);
604    cnt = bsscanf(helloreq, hello, &Job);
605    printf("cnt=%d Agent=%s\n", cnt, Job);
606 #endif
607    cnt = bsscanf(media, OK_media,
608                vol.VolCatName,
609                &vol.VolCatJobs, &vol.VolCatFiles,
610                &vol.VolCatBlocks, &vol.VolCatBytes,
611                &vol.VolCatMounts, &vol.VolCatErrors,
612                &vol.VolCatWrites, &vol.VolCatMaxBytes,
613                &vol.VolCatCapacityBytes, vol.VolCatStatus,
614                &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
615                &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
616    printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);
617
618 }
619
620 #endif