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