2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
17 * scan.c -- scanning routines for Bacula
19 * Kern Sibbald, MM separated from util.c MMIII
25 #include "findlib/find.h"
28 * Strip leading space from command line arguments
30 void strip_leading_space(char *str)
34 while (B_ISSPACE(*p)) {
43 * Strip any trailing junk from the command
45 void strip_trailing_junk(char *cmd)
50 * Strip trailing junk from command
52 p = cmd + strlen(cmd) - 1;
53 while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' ')) {
59 * Strip any trailing newline characters from the string
61 void strip_trailing_newline(char *cmd)
65 p = cmd + strlen(cmd) - 1;
66 while ((p >= cmd) && (*p == '\n' || *p == '\r')) {
72 * Strip any trailing slashes from a directory path
74 void strip_trailing_slashes(char *dir)
79 * Strip trailing slashes
81 p = dir + strlen(dir) - 1;
82 while (p >= dir && IsPathSeparator(*p)) {
89 * Returns: 0 on failure (EOF)
91 * new address in passed parameter
93 bool skip_spaces(char **msg)
99 while (*p && B_ISSPACE(*p)) {
103 return *p ? true : false;
108 * Returns: 0 on failure (EOF)
110 * new address in passed parameter
112 bool skip_nonspaces(char **msg)
119 while (*p && !B_ISSPACE(*p)) {
123 return *p ? true : false;
127 * Folded search for string - case insensitive
129 int fstrsch(const char *a, const char *b) /* folded case search */
136 while (*s1) { /* do it the fast way */
137 if ((*s1++ | 0x20) != (*s2++ | 0x20))
138 return 0; /* failed */
140 while (*a) { /* do it over the correct slow way */
141 if (B_ISUPPER(c1 = *a)) {
142 c1 = tolower((int)c1);
144 if (B_ISUPPER(c2 = *b)) {
145 c2 = tolower((int)c2);
157 * Return next argument from command line. Note, this
158 * routine is destructive because it stored 0 at the end
160 * Called with pointer to pointer to command line. This
161 * pointer is updated to point to the remainder of the
164 * Returns pointer to next argument -- don't store the result
165 * in the pointer you passed as an argument ...
166 * The next argument is terminated by a space unless within
167 * quotes. Double quote characters (unless preceded by a \) are
171 char *next_arg(char **s)
174 bool in_quote = false;
176 /* skip past spaces to next arg */
177 for (p=*s; *p && B_ISSPACE(*p); ) {
180 Dmsg1(900, "Next arg=%s\n", p);
181 for (n = q = p; *p ; ) {
182 if (*p == '\\') { /* slash? */
183 p++; /* yes, skip it */
191 if (*p == '"') { /* start or end of quote */
193 in_quote = !in_quote; /* change state */
196 if (!in_quote && B_ISSPACE(*p)) { /* end of field */
204 Dmsg2(900, "End arg=%s next=%s\n", n, p);
209 * This routine parses the input command line.
210 * It makes a copy in args, then builds an
211 * argc, argk, argv list where:
213 * argc = count of arguments
214 * argk[i] = argument keyword (part preceding =)
215 * argv[i] = argument value (part after =)
217 * example: arg1 arg2=abc arg3=
227 int parse_args(POOLMEM *cmd, POOLMEM **args, int *argc,
228 char **argk, char **argv, int max_args)
232 parse_args_only(cmd, args, argc, argk, argv, max_args);
234 /* Separate keyword and value */
235 for (int i=0; i < *argc; i++) {
236 p = strchr(argk[i], '=');
238 *p++ = 0; /* terminate keyword and point to value */
240 argv[i] = p; /* save ptr to value or NULL */
243 for (int i=0; i < *argc; i++) {
244 Pmsg3(000, "Arg %d: kw=%s val=%s\n", i, argk[i], argv[i]?argv[i]:"NULL");
252 * This routine parses the input command line.
253 * It makes a copy in args, then builds an
254 * argc, argk, but no argv (values).
255 * This routine is useful for scanning command lines where the data
256 * is a filename and no keywords are expected. If we scan a filename
257 * for keywords, any = in the filename will be interpreted as the
258 * end of a keyword, and this is not good.
260 * argc = count of arguments
261 * argk[i] = argument keyword (part preceding =)
264 * example: arg1 arg2=abc arg3=
274 int parse_args_only(POOLMEM *cmd, POOLMEM **args, int *argc,
275 char **argk, char **argv, int max_args)
279 pm_strcpy(args, cmd);
280 strip_trailing_junk(*args);
284 * Pick up all arguments
286 while (*argc < max_args) {
290 argv[(*argc)++] = NULL;
299 * Given a full filename, split it into its path
300 * and filename parts. They are returned in pool memory
301 * in the arguments provided.
303 void split_path_and_filename(const char *fname, POOLMEM **path, int *pnl,
304 POOLMEM **file, int *fnl)
308 int len = slen = strlen(fname);
311 * Find path without the filename.
312 * I.e. everything after the last / is a "filename".
313 * OK, maybe it is a directory name, but we treat it like
314 * a filename. If we don't find a / then the whole name
315 * must be a path name (e.g. c:).
318 /* "strip" any trailing slashes */
319 while (slen > 1 && IsPathSeparator(*f)) {
323 /* Walk back to last slash -- begin of filename */
324 while (slen > 0 && !IsPathSeparator(*f)) {
328 if (IsPathSeparator(*f)) { /* did we find a slash? */
329 f++; /* yes, point to filename */
330 } else { /* no, whole thing must be path name */
333 Dmsg2(200, "after strip len=%d f=%s\n", len, f);
334 *fnl = fname - f + len;
336 *file = check_pool_memory_size(*file, *fnl+1);
337 memcpy(*file, f, *fnl); /* copy filename */
343 *path = check_pool_memory_size(*path, *pnl+1);
344 memcpy(*path, fname, *pnl);
348 Dmsg2(200, "pnl=%d fnl=%d\n", *pnl, *fnl);
349 Dmsg3(200, "split fname=%s path=%s file=%s\n", fname, *path, *file);
353 * Extremely simple sscanf. Handles only %(u,d,ld,qd,qu,lu,lld,llu,c,nns)
355 * Note, BIG is the default maximum length when no length
356 * has been specified for %s. If it is not big enough, then
357 * simply add a length such as %10000s.
359 const int BIG = 1000;
360 int bsscanf(const char *buf, const char *fmt, ...)
373 while (*fmt && !error) {
374 // Dmsg1(000, "fmt=%c\n", *fmt);
377 // Dmsg1(000, "Got %% nxt=%c\n", *fmt);
382 while (B_ISDIGIT(*buf)) {
383 value = B_TIMES10(value) + *buf++ - '0';
385 vp = (void *)va_arg(ap, void *);
386 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
388 *((int *)vp) = (int)value;
390 *((uint32_t *)vp) = (uint32_t)value;
391 // Dmsg0(000, "Store 32 bit int\n");
393 *((uint64_t *)vp) = (uint64_t)value;
394 // Dmsg0(000, "Store 64 bit int\n");
407 while (B_ISDIGIT(*buf)) {
408 value = B_TIMES10(value) + *buf++ - '0';
413 vp = (void *)va_arg(ap, void *);
414 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
416 *((int *)vp) = (int)value;
418 *((int32_t *)vp) = (int32_t)value;
419 // Dmsg0(000, "Store 32 bit int\n");
421 *((int64_t *)vp) = (int64_t)value;
422 // Dmsg0(000, "Store 64 bit int\n");
428 // Dmsg0(000, "got l\n");
434 if (*fmt == 'd' || *fmt == 'u') {
437 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
442 if (*fmt == 'd' || *fmt == 'u') {
445 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
449 // Dmsg1(000, "Store string max_len=%d\n", max_len);
450 cp = (char *)va_arg(ap, char *);
451 while (*buf && !B_ISSPACE(*buf) && max_len-- > 0) {
459 cp = (char *)va_arg(ap, char *);
471 while (B_ISDIGIT(*fmt)) {
472 max_len = B_TIMES10(max_len) + *fmt++ - '0';
474 // Dmsg1(000, "Default max_len=%d\n", max_len);
478 // Dmsg1(000, "Default c=%c\n", *fmt);
480 break; /* error: unknown format */
484 /* White space eats zero or more whitespace */
485 } else if (B_ISSPACE(*fmt)) {
487 while (B_ISSPACE(*buf)) {
490 /* Plain text must match */
491 } else if (*buf++ != *fmt++) {
492 // Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
498 // Dmsg2(000, "Error=%d count=%d\n", error, count);
506 int main(int argc, char *argv[])
511 uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
514 char *helloreq= "Hello *UserAgent* calling\n";
515 char *hello = "Hello %127s calling\n";
517 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
518 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
519 "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
520 "StartBlock=%u EndBlock=%u\n";
521 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
522 " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
523 " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
524 " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
525 " VolReadTime=%" lld " VolWriteTime=%" lld;
527 "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";
528 struct VOLUME_CAT_INFO {
529 /* Media info for the current Volume */
530 uint32_t VolCatJobs; /* number of jobs on this Volume */
531 uint32_t VolCatFiles; /* Number of files */
532 uint32_t VolCatBlocks; /* Number of blocks */
533 uint64_t VolCatBytes; /* Number of bytes written */
534 uint32_t VolCatMounts; /* Number of mounts this volume */
535 uint32_t VolCatErrors; /* Number of errors this volume */
536 uint32_t VolCatWrites; /* Number of writes this volume */
537 uint32_t VolCatReads; /* Number of reads this volume */
538 uint64_t VolCatRBytes; /* Number of bytes read */
539 uint32_t VolCatRecycles; /* Number of recycles this volume */
540 int32_t Slot; /* Slot in changer */
541 bool InChanger; /* Set if vol in current magazine */
542 uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
543 uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
544 uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
545 uint64_t VolCatCapacityBytes; /* capacity estimate */
546 uint64_t VolReadTime; /* time spent reading */
547 uint64_t VolWriteTime; /* time spent writing this Volume */
548 char VolCatStatus[20]; /* Volume status */
549 char VolCatName[MAX_NAME_LENGTH]; /* Desired volume to mount */
551 struct VOLUME_CAT_INFO vol;
554 bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
555 printf("%s %d %lld\n", buf, val32, val64);
558 cnt = bsscanf(catreq, Create_job_media, &Job,
559 &FirstIndex, &LastIndex, &StartFile, &EndFile,
560 &StartBlock, &EndBlock);
561 printf("cnt=%d Job=%s\n", cnt, Job);
562 cnt = bsscanf(helloreq, hello, &Job);
563 printf("cnt=%d Agent=%s\n", cnt, Job);
565 cnt = bsscanf(media, OK_media,
567 &vol.VolCatJobs, &vol.VolCatFiles,
568 &vol.VolCatBlocks, &vol.VolCatBytes,
569 &vol.VolCatMounts, &vol.VolCatErrors,
570 &vol.VolCatWrites, &vol.VolCatMaxBytes,
571 &vol.VolCatCapacityBytes, vol.VolCatStatus,
572 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
573 &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
574 printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);