]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/scan.c
Win32 fix -- remove debug O_NONBLOCK code
[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       }
410    }
411    va_end(ap);
412 // Dmsg2(000, "Error=%d count=%d\n", error, count);
413    if (error) {
414       count = -1;
415    }
416    return count;
417 }
418
419 #ifdef TEST_PROGRAM
420 int main(int argc, char *argv[])
421 {
422    char buf[100];
423    uint32_t val32;
424    uint64_t val64;
425    uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
426    char Job[200];
427    int cnt;
428    char *helloreq= "Hello *UserAgent* calling\n";
429    char *hello = "Hello %127s calling\n";
430    char *catreq =
431 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
432 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
433   "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
434   "StartBlock=%u EndBlock=%u\n";
435 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
436    " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
437    " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
438    " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
439    " VolReadTime=%" lld " VolWriteTime=%" lld;
440    char *media =
441 "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";
442 struct VOLUME_CAT_INFO {
443    /* Media info for the current Volume */
444    uint32_t VolCatJobs;               /* number of jobs on this Volume */
445    uint32_t VolCatFiles;              /* Number of files */
446    uint32_t VolCatBlocks;             /* Number of blocks */
447    uint64_t VolCatBytes;              /* Number of bytes written */
448    uint32_t VolCatMounts;             /* Number of mounts this volume */
449    uint32_t VolCatErrors;             /* Number of errors this volume */
450    uint32_t VolCatWrites;             /* Number of writes this volume */
451    uint32_t VolCatReads;              /* Number of reads this volume */
452    uint64_t VolCatRBytes;             /* Number of bytes read */
453    uint32_t VolCatRecycles;           /* Number of recycles this volume */
454    int32_t  Slot;                     /* Slot in changer */
455    bool     InChanger;                /* Set if vol in current magazine */
456    uint32_t VolCatMaxJobs;            /* Maximum Jobs to write to volume */
457    uint32_t VolCatMaxFiles;           /* Maximum files to write to volume */
458    uint64_t VolCatMaxBytes;           /* Max bytes to write to volume */
459    uint64_t VolCatCapacityBytes;      /* capacity estimate */
460    uint64_t VolReadTime;              /* time spent reading */
461    uint64_t VolWriteTime;             /* time spent writing this Volume */
462    char VolCatStatus[20];             /* Volume status */
463    char VolCatName[MAX_NAME_LENGTH];  /* Desired volume to mount */
464 };
465    struct VOLUME_CAT_INFO vol;
466
467 #ifdef xxx
468    bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
469    printf("%s %d %lld\n", buf, val32, val64);
470
471    *Job=0;
472    cnt = bsscanf(catreq, Create_job_media, &Job,
473       &FirstIndex, &LastIndex, &StartFile, &EndFile,
474       &StartBlock, &EndBlock);
475    printf("cnt=%d Job=%s\n", cnt, Job);
476    cnt = bsscanf(helloreq, hello, &Job);
477    printf("cnt=%d Agent=%s\n", cnt, Job);
478 #endif
479    cnt = bsscanf(media, OK_media,
480                vol.VolCatName,
481                &vol.VolCatJobs, &vol.VolCatFiles,
482                &vol.VolCatBlocks, &vol.VolCatBytes,
483                &vol.VolCatMounts, &vol.VolCatErrors,
484                &vol.VolCatWrites, &vol.VolCatMaxBytes,
485                &vol.VolCatCapacityBytes, vol.VolCatStatus,
486                &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
487                &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
488    printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);
489
490 }
491
492 #endif