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