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