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