]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/scan.c
Eliminate dependency on man2html.
[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 == 0) {
343                *((int *)vp) = (int)value;
344             } else if (l == 1) {
345                *((uint32_t *)vp) = (uint32_t)value;
346 //             Dmsg0(000, "Store 32 bit int\n");
347             } else {
348                *((uint64_t *)vp) = (uint64_t)value;
349 //             Dmsg0(000, "Store 64 bit int\n");
350             }
351             count++;
352             l = 0;
353             break;
354          case 'l':
355 //          Dmsg0(000, "got l\n");
356             l = 1;
357             if (*fmt == 'l') {
358                l++;
359                fmt++;
360             }
361             if (*fmt == 'd' || *fmt == 'u') {
362                goto switch_top;
363             }
364 //          Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
365             error = true;
366             break;
367          case 'q':
368             l = 2;
369             if (*fmt == 'd' || *fmt == 'u') {
370                goto switch_top;
371             }
372 //          Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
373             error = true;
374             break;
375          case 's':
376 //          Dmsg1(000, "Store string max_len=%d\n", max_len);
377             cp = (char *)va_arg(ap, char *);
378             while (*buf && !B_ISSPACE(*buf) && max_len-- > 0) {
379                *cp++ = *buf++;
380             }
381             *cp = 0;
382             count++;
383             max_len = BIG;
384             break;
385          case 'c':
386             cp = (char *)va_arg(ap, char *);
387             *cp = *buf++;
388             count++;
389             break;
390          case '%':
391             if (*buf++ != '%') {
392                error = true;
393             }
394             break;
395          default:
396             fmt--;
397             max_len = 0;
398             while (B_ISDIGIT(*fmt)) {
399                max_len = B_TIMES10(max_len) + *fmt++ - '0';
400             }
401 //          Dmsg1(000, "Default max_len=%d\n", max_len);
402             if (*fmt == 's') {
403                goto switch_top;
404             }
405 //          Dmsg1(000, "Default c=%c\n", *fmt);
406             error = true;
407             break;                    /* error: unknown format */
408          }
409          continue;
410
411       /* White space eats zero or more whitespace */
412       } else if (B_ISSPACE(*fmt)) {
413          fmt++;
414          while (B_ISSPACE(*buf)) {
415             buf++;
416          }
417       /* Plain text must match */
418       } else if (*buf++ != *fmt++) {
419 //       Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
420          error = true;
421          break;
422       }
423    }
424    va_end(ap);
425 // Dmsg2(000, "Error=%d count=%d\n", error, count);
426    if (error) {
427       count = -1;
428    }
429    return count;
430 }
431
432 #ifdef TEST_PROGRAM
433 int main(int argc, char *argv[])
434 {
435    char buf[100];
436    uint32_t val32;
437    uint64_t val64;
438    uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
439    char Job[200];
440    int cnt;
441    char *helloreq= "Hello *UserAgent* calling\n";
442    char *hello = "Hello %127s calling\n";
443    char *catreq =
444 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
445 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
446   "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
447   "StartBlock=%u EndBlock=%u\n";
448 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
449    " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
450    " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
451    " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
452    " VolReadTime=%" lld " VolWriteTime=%" lld;
453    char *media =
454 "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";
455 struct VOLUME_CAT_INFO {
456    /* Media info for the current Volume */
457    uint32_t VolCatJobs;               /* number of jobs on this Volume */
458    uint32_t VolCatFiles;              /* Number of files */
459    uint32_t VolCatBlocks;             /* Number of blocks */
460    uint64_t VolCatBytes;              /* Number of bytes written */
461    uint32_t VolCatMounts;             /* Number of mounts this volume */
462    uint32_t VolCatErrors;             /* Number of errors this volume */
463    uint32_t VolCatWrites;             /* Number of writes this volume */
464    uint32_t VolCatReads;              /* Number of reads this volume */
465    uint64_t VolCatRBytes;             /* Number of bytes read */
466    uint32_t VolCatRecycles;           /* Number of recycles this volume */
467    int32_t  Slot;                     /* Slot in changer */
468    bool     InChanger;                /* Set if vol in current magazine */
469    uint32_t VolCatMaxJobs;            /* Maximum Jobs to write to volume */
470    uint32_t VolCatMaxFiles;           /* Maximum files to write to volume */
471    uint64_t VolCatMaxBytes;           /* Max bytes to write to volume */
472    uint64_t VolCatCapacityBytes;      /* capacity estimate */
473    uint64_t VolReadTime;              /* time spent reading */
474    uint64_t VolWriteTime;             /* time spent writing this Volume */
475    char VolCatStatus[20];             /* Volume status */
476    char VolCatName[MAX_NAME_LENGTH];  /* Desired volume to mount */
477 };
478    struct VOLUME_CAT_INFO vol;
479
480 #ifdef xxx
481    bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
482    printf("%s %d %lld\n", buf, val32, val64);
483
484    *Job=0;
485    cnt = bsscanf(catreq, Create_job_media, &Job,
486       &FirstIndex, &LastIndex, &StartFile, &EndFile,
487       &StartBlock, &EndBlock);
488    printf("cnt=%d Job=%s\n", cnt, Job);
489    cnt = bsscanf(helloreq, hello, &Job);
490    printf("cnt=%d Agent=%s\n", cnt, Job);
491 #endif
492    cnt = bsscanf(media, OK_media,
493                vol.VolCatName,
494                &vol.VolCatJobs, &vol.VolCatFiles,
495                &vol.VolCatBlocks, &vol.VolCatBytes,
496                &vol.VolCatMounts, &vol.VolCatErrors,
497                &vol.VolCatWrites, &vol.VolCatMaxBytes,
498                &vol.VolCatCapacityBytes, vol.VolCatStatus,
499                &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
500                &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
501    printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);
502
503 }
504
505 #endif