2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many 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 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * scan.c -- scanning routines for Bacula
22 * Kern Sibbald, MM separated from util.c MMIII
29 #include "findlib/find.h"
31 /* Strip leading space from command line arguments */
32 void strip_leading_space(char *str)
35 while (B_ISSPACE(*p)) {
44 /* Strip any trailing junk from the command */
45 void strip_trailing_junk(char *cmd)
49 /* strip trailing junk from command */
50 p = cmd - 1 + strlen(cmd);
51 while ((p >= cmd) && (B_ISSPACE(*p) || *p == '\n' || *p == '\r')) {
56 /* Strip any trailing newline characters from the string */
57 void strip_trailing_newline(char *cmd)
60 p = cmd - 1 + strlen(cmd);
61 while ((p >= cmd) && (*p == '\n' || *p == '\r')) *p-- = 0;
64 /* Strip any trailing slashes from a directory path */
65 void strip_trailing_slashes(char *dir)
69 /* strip trailing slashes */
70 p = dir -1 + strlen(dir);
71 while (p >= dir && IsPathSeparator(*p)) *p-- = 0;
76 * Returns: 0 on failure (EOF)
78 * new address in passed parameter
80 bool skip_spaces(char **msg)
86 while (*p && B_ISSPACE(*p)) {
90 return *p ? true : false;
95 * Returns: 0 on failure (EOF)
97 * new address in passed parameter
99 bool skip_nonspaces(char **msg)
106 while (*p && !B_ISSPACE(*p)) {
110 return *p ? true : false;
113 /* folded search for string - case insensitive */
115 fstrsch(const char *a, const char *b) /* folded case search */
122 while (*s1) { /* do it the fast way */
123 if ((*s1++ | 0x20) != (*s2++ | 0x20))
124 return 0; /* failed */
126 while (*a) { /* do it over the correct slow way */
127 if (B_ISUPPER(c1 = *a)) {
128 c1 = tolower((int)c1);
130 if (B_ISUPPER(c2 = *b)) {
131 c2 = tolower((int)c2);
144 * Return next argument from command line. Note, this
145 * routine is destructive because it stored 0 at the end
147 * Called with pointer to pointer to command line. This
148 * pointer is updated to point to the remainder of the
151 * Returns pointer to next argument -- don't store the result
152 * in the pointer you passed as an argument ...
153 * The next argument is terminated by a space unless within
154 * quotes. Double quote characters (unless preceded by a \) are
158 char *next_arg(char **s)
161 bool in_quote = false;
163 /* skip past spaces to next arg */
164 for (p=*s; *p && B_ISSPACE(*p); ) {
167 Dmsg1(900, "Next arg=%s\n", p);
168 for (n = q = p; *p ; ) {
169 if (*p == '\\') { /* slash? */
170 p++; /* yes, skip it */
178 if (*p == '"') { /* start or end of quote */
180 in_quote = !in_quote; /* change state */
183 if (!in_quote && B_ISSPACE(*p)) { /* end of field */
191 Dmsg2(900, "End arg=%s next=%s\n", n, p);
196 * This routine parses the input command line.
197 * It makes a copy in args, then builds an
198 * argc, argk, argv list where:
200 * argc = count of arguments
201 * argk[i] = argument keyword (part preceding =)
202 * argv[i] = argument value (part after =)
204 * example: arg1 arg2=abc arg3=
214 int parse_args(POOLMEM *cmd, POOLMEM **args, int *argc,
215 char **argk, char **argv, int max_args)
219 parse_args_only(cmd, args, argc, argk, argv, max_args);
221 /* Separate keyword and value */
222 for (int i=0; i < *argc; i++) {
223 p = strchr(argk[i], '=');
225 *p++ = 0; /* terminate keyword and point to value */
227 argv[i] = p; /* save ptr to value or NULL */
230 for (int i=0; i < *argc; i++) {
231 Pmsg3(000, "Arg %d: kw=%s val=%s\n", i, argk[i], argv[i]?argv[i]:"NULL");
239 * This routine parses the input command line.
240 * It makes a copy in args, then builds an
241 * argc, argk, but no argv (values).
242 * This routine is useful for scanning command lines where the data
243 * is a filename and no keywords are expected. If we scan a filename
244 * for keywords, any = in the filename will be interpreted as the
245 * end of a keyword, and this is not good.
247 * argc = count of arguments
248 * argk[i] = argument keyword (part preceding =)
251 * example: arg1 arg2=abc arg3=
261 int parse_args_only(POOLMEM *cmd, POOLMEM **args, int *argc,
262 char **argk, char **argv, int max_args)
266 pm_strcpy(args, cmd);
267 strip_trailing_junk(*args);
270 /* Pick up all arguments */
271 while (*argc < max_args) {
275 argv[(*argc)++] = NULL;
285 * Given a full filename, split it into its path
286 * and filename parts. They are returned in pool memory
287 * in the arguments provided.
289 void split_path_and_filename(const char *fname, POOLMEM **path, int *pnl,
290 POOLMEM **file, int *fnl)
294 int len = slen = strlen(fname);
297 * Find path without the filename.
298 * I.e. everything after the last / is a "filename".
299 * OK, maybe it is a directory name, but we treat it like
300 * a filename. If we don't find a / then the whole name
301 * must be a path name (e.g. c:).
304 /* "strip" any trailing slashes */
305 while (slen > 1 && IsPathSeparator(*f)) {
309 /* Walk back to last slash -- begin of filename */
310 while (slen > 0 && !IsPathSeparator(*f)) {
314 if (IsPathSeparator(*f)) { /* did we find a slash? */
315 f++; /* yes, point to filename */
316 } else { /* no, whole thing must be path name */
319 Dmsg2(200, "after strip len=%d f=%s\n", len, f);
320 *fnl = fname - f + len;
322 *file = check_pool_memory_size(*file, *fnl+1);
323 memcpy(*file, f, *fnl); /* copy filename */
329 *path = check_pool_memory_size(*path, *pnl+1);
330 memcpy(*path, fname, *pnl);
334 Dmsg2(200, "pnl=%d fnl=%d\n", *pnl, *fnl);
335 Dmsg3(200, "split fname=%s path=%s file=%s\n", fname, *path, *file);
339 * Extremely simple sscanf. Handles only %(u,d,ld,qd,qu,lu,lld,llu,c,nns)
341 * Note, BIG is the default maximum length when no length
342 * has been specified for %s. If it is not big enough, then
343 * simply add a length such as %10000s.
345 const int BIG = 1000;
346 int bsscanf(const char *buf, const char *fmt, ...)
359 while (*fmt && !error) {
360 // Dmsg1(000, "fmt=%c\n", *fmt);
363 // Dmsg1(000, "Got %% nxt=%c\n", *fmt);
368 while (B_ISDIGIT(*buf)) {
369 value = B_TIMES10(value) + *buf++ - '0';
371 vp = (void *)va_arg(ap, void *);
372 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
374 *((int *)vp) = (int)value;
376 *((uint32_t *)vp) = (uint32_t)value;
377 // Dmsg0(000, "Store 32 bit int\n");
379 *((uint64_t *)vp) = (uint64_t)value;
380 // Dmsg0(000, "Store 64 bit int\n");
393 while (B_ISDIGIT(*buf)) {
394 value = B_TIMES10(value) + *buf++ - '0';
399 vp = (void *)va_arg(ap, void *);
400 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
402 *((int *)vp) = (int)value;
404 *((int32_t *)vp) = (int32_t)value;
405 // Dmsg0(000, "Store 32 bit int\n");
407 *((int64_t *)vp) = (int64_t)value;
408 // Dmsg0(000, "Store 64 bit int\n");
414 // Dmsg0(000, "got l\n");
420 if (*fmt == 'd' || *fmt == 'u') {
423 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
428 if (*fmt == 'd' || *fmt == 'u') {
431 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
435 // Dmsg1(000, "Store string max_len=%d\n", max_len);
436 cp = (char *)va_arg(ap, char *);
437 while (*buf && !B_ISSPACE(*buf) && max_len-- > 0) {
445 cp = (char *)va_arg(ap, char *);
457 while (B_ISDIGIT(*fmt)) {
458 max_len = B_TIMES10(max_len) + *fmt++ - '0';
460 // Dmsg1(000, "Default max_len=%d\n", max_len);
464 // Dmsg1(000, "Default c=%c\n", *fmt);
466 break; /* error: unknown format */
470 /* White space eats zero or more whitespace */
471 } else if (B_ISSPACE(*fmt)) {
473 while (B_ISSPACE(*buf)) {
476 /* Plain text must match */
477 } else if (*buf++ != *fmt++) {
478 // Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
484 // Dmsg2(000, "Error=%d count=%d\n", error, count);
492 int main(int argc, char *argv[])
497 uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
500 char *helloreq= "Hello *UserAgent* calling\n";
501 char *hello = "Hello %127s calling\n";
503 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
504 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
505 "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
506 "StartBlock=%u EndBlock=%u\n";
507 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
508 " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
509 " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
510 " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
511 " VolReadTime=%" lld " VolWriteTime=%" lld;
513 "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";
514 struct VOLUME_CAT_INFO {
515 /* Media info for the current Volume */
516 uint32_t VolCatJobs; /* number of jobs on this Volume */
517 uint32_t VolCatFiles; /* Number of files */
518 uint32_t VolCatBlocks; /* Number of blocks */
519 uint64_t VolCatBytes; /* Number of bytes written */
520 uint32_t VolCatMounts; /* Number of mounts this volume */
521 uint32_t VolCatErrors; /* Number of errors this volume */
522 uint32_t VolCatWrites; /* Number of writes this volume */
523 uint32_t VolCatReads; /* Number of reads this volume */
524 uint64_t VolCatRBytes; /* Number of bytes read */
525 uint32_t VolCatRecycles; /* Number of recycles this volume */
526 int32_t Slot; /* Slot in changer */
527 bool InChanger; /* Set if vol in current magazine */
528 uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
529 uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
530 uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
531 uint64_t VolCatCapacityBytes; /* capacity estimate */
532 uint64_t VolReadTime; /* time spent reading */
533 uint64_t VolWriteTime; /* time spent writing this Volume */
534 char VolCatStatus[20]; /* Volume status */
535 char VolCatName[MAX_NAME_LENGTH]; /* Desired volume to mount */
537 struct VOLUME_CAT_INFO vol;
540 bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
541 printf("%s %d %lld\n", buf, val32, val64);
544 cnt = bsscanf(catreq, Create_job_media, &Job,
545 &FirstIndex, &LastIndex, &StartFile, &EndFile,
546 &StartBlock, &EndBlock);
547 printf("cnt=%d Job=%s\n", cnt, Job);
548 cnt = bsscanf(helloreq, hello, &Job);
549 printf("cnt=%d Agent=%s\n", cnt, Job);
551 cnt = bsscanf(media, OK_media,
553 &vol.VolCatJobs, &vol.VolCatFiles,
554 &vol.VolCatBlocks, &vol.VolCatBytes,
555 &vol.VolCatMounts, &vol.VolCatErrors,
556 &vol.VolCatWrites, &vol.VolCatMaxBytes,
557 &vol.VolCatCapacityBytes, vol.VolCatStatus,
558 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
559 &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
560 printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);