2 * scan.c -- scanning routines for Bacula
4 * Kern Sibbald, MM separated from util.c MMIII
9 Copyright (C) 2000-2005 Kern Sibbald
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.
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.
26 #include "findlib/find.h"
28 /* Strip leading space from command line arguments */
29 void strip_leading_space(char *str)
32 while (B_ISSPACE(*p)) {
41 /* Strip any trailing junk from the command */
42 void strip_trailing_junk(char *cmd)
45 p = cmd + strlen(cmd) - 1;
47 /* strip trailing junk from command */
48 while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
52 /* Strip any trailing newline characters from the string */
53 void strip_trailing_newline(char *cmd)
56 p = cmd + strlen(cmd) - 1;
58 while ((p >= cmd) && (*p == '\n' || *p == '\r'))
62 /* Strip any trailing slashes from a directory path */
63 void strip_trailing_slashes(char *dir)
66 p = dir + strlen(dir) - 1;
68 /* strip trailing slashes */
69 while ((p >= dir) && (*p == '/'))
75 * Returns: 0 on failure (EOF)
77 * new address in passed parameter
79 bool skip_spaces(char **msg)
85 while (*p && B_ISSPACE(*p)) {
89 return *p ? true : false;
94 * Returns: 0 on failure (EOF)
96 * new address in passed parameter
98 bool skip_nonspaces(char **msg)
105 while (*p && !B_ISSPACE(*p)) {
109 return *p ? true : false;
112 /* folded search for string - case insensitive */
114 fstrsch(const char *a, const char *b) /* folded case search */
121 while (*s1) { /* do it the fast way */
122 if ((*s1++ | 0x20) != (*s2++ | 0x20))
123 return 0; /* failed */
125 while (*a) { /* do it over the correct slow way */
126 if (B_ISUPPER(c1 = *a)) {
127 c1 = tolower((int)c1);
129 if (B_ISUPPER(c2 = *b)) {
130 c2 = tolower((int)c2);
143 * Return next argument from command line. Note, this
144 * routine is destructive.
146 char *next_arg(char **s)
149 bool in_quote = false;
151 /* skip past spaces to next arg */
152 for (p=*s; *p && B_ISSPACE(*p); ) {
155 Dmsg1(900, "Next arg=%s\n", p);
156 for (n = q = p; *p ; ) {
166 if (*p == '"') { /* start or end of quote */
168 p++; /* skip quote */
176 if (!in_quote && B_ISSPACE(*p)) { /* end of field */
184 Dmsg2(900, "End arg=%s next=%s\n", n, p);
189 * This routine parses the input command line.
190 * It makes a copy in args, then builds an
191 * argc, argv like list where
193 * argc = count of arguments
194 * argk[i] = argument keyword (part preceding =)
195 * argv[i] = argument value (part after =)
197 * example: arg1 arg2=abc arg3=
208 int parse_args(POOLMEM *cmd, POOLMEM **args, int *argc,
209 char **argk, char **argv, int max_args)
213 pm_strcpy(args, cmd);
214 strip_trailing_junk(*args);
217 /* Pick up all arguments */
218 while (*argc < max_args) {
222 argv[(*argc)++] = NULL;
227 /* Separate keyword and value */
228 for (int i=0; i < *argc; i++) {
229 p = strchr(argk[i], '=');
231 *p++ = 0; /* terminate keyword and point to value */
232 /* Unquote quoted values */
234 for (n = q = ++p; *p && *p != '"'; ) {
240 *q = 0; /* terminate string */
241 p = n; /* point to string */
243 if (strlen(p) > MAX_NAME_LENGTH-1) {
244 p[MAX_NAME_LENGTH-1] = 0; /* truncate to max len */
247 argv[i] = p; /* save ptr to value or NULL */
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");
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.
262 void split_path_and_filename(const char *fname, POOLMEM **path, int *pnl,
263 POOLMEM **file, int *fnl)
267 int len = slen = strlen(fname);
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:).
277 /* "strip" any trailing slashes */
278 while (slen > 1 && *f == '/') {
282 /* Walk back to last slash -- begin of filename */
283 while (slen > 0 && *f != '/') {
287 if (*f == '/') { /* did we find a slash? */
288 f++; /* yes, point to filename */
289 } else { /* no, whole thing must be path name */
292 Dmsg2(200, "after strip len=%d f=%s\n", len, f);
293 *fnl = fname - f + len;
295 *file = check_pool_memory_size(*file, *fnl+1);
296 memcpy(*file, f, *fnl); /* copy filename */
302 *path = check_pool_memory_size(*path, *pnl+1);
303 memcpy(*path, fname, *pnl);
307 Dmsg2(200, "pnl=%d fnl=%d\n", *pnl, *fnl);
308 Dmsg3(200, "split fname=%s path=%s file=%s\n", fname, *path, *file);
312 * Extremely simple sscanf. Handles only %(u,d,ld,qd,qu,lu,lld,llu,c,nns)
314 const int BIG = 1000;
315 int bsscanf(const char *buf, const char *fmt, ...)
327 while (*fmt && !error) {
328 // Dmsg1(000, "fmt=%c\n", *fmt);
331 // Dmsg1(000, "Got %% nxt=%c\n", *fmt);
337 while (B_ISDIGIT(*buf)) {
338 value = B_TIMES10(value) + *buf++ - '0';
340 vp = (void *)va_arg(ap, void *);
341 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
343 *((int *)vp) = (int)value;
345 *((uint32_t *)vp) = (uint32_t)value;
346 // Dmsg0(000, "Store 32 bit int\n");
348 *((uint64_t *)vp) = (uint64_t)value;
349 // Dmsg0(000, "Store 64 bit int\n");
355 // Dmsg0(000, "got l\n");
361 if (*fmt == 'd' || *fmt == 'u') {
364 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
369 if (*fmt == 'd' || *fmt == 'u') {
372 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
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) {
386 cp = (char *)va_arg(ap, char *);
398 while (B_ISDIGIT(*fmt)) {
399 max_len = B_TIMES10(max_len) + *fmt++ - '0';
401 // Dmsg1(000, "Default max_len=%d\n", max_len);
405 // Dmsg1(000, "Default c=%c\n", *fmt);
407 break; /* error: unknown format */
411 /* White space eats zero or more whitespace */
412 } else if (B_ISSPACE(*fmt)) {
414 while (B_ISSPACE(*buf)) {
417 /* Plain text must match */
418 } else if (*buf++ != *fmt++) {
419 // Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
425 // Dmsg2(000, "Error=%d count=%d\n", error, count);
433 int main(int argc, char *argv[])
438 uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
441 char *helloreq= "Hello *UserAgent* calling\n";
442 char *hello = "Hello %127s calling\n";
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;
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 */
478 struct VOLUME_CAT_INFO vol;
481 bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
482 printf("%s %d %lld\n", buf, val32, val64);
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);
492 cnt = bsscanf(media, OK_media,
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);