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