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