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 *((uint32_t *)vp) = (uint32_t)value;
344 // Dmsg0(000, "Store 32 bit int\n");
346 *((uint64_t *)vp) = (uint64_t)value;
347 // Dmsg0(000, "Store 64 bit int\n");
353 // Dmsg0(000, "got l\n");
359 if (*fmt == 'd' || *fmt == 'u') {
362 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
367 if (*fmt == 'd' || *fmt == 'u') {
370 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
374 // Dmsg1(000, "Store string max_len=%d\n", max_len);
375 cp = (char *)va_arg(ap, char *);
376 while (*buf && !B_ISSPACE(*buf) && max_len-- > 0) {
384 cp = (char *)va_arg(ap, char *);
396 while (B_ISDIGIT(*fmt)) {
397 max_len = B_TIMES10(max_len) + *fmt++ - '0';
399 // Dmsg1(000, "Default max_len=%d\n", max_len);
403 // Dmsg1(000, "Default c=%c\n", *fmt);
405 break; /* error: unknown format */
409 /* White space eats zero or more whitespace */
410 } else if (B_ISSPACE(*fmt)) {
412 while (B_ISSPACE(*buf)) {
415 /* Plain text must match */
416 } else if (*buf++ != *fmt++) {
417 // Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
423 // Dmsg2(000, "Error=%d count=%d\n", error, count);
431 int main(int argc, char *argv[])
436 uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
439 char *helloreq= "Hello *UserAgent* calling\n";
440 char *hello = "Hello %127s calling\n";
442 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
443 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
444 "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
445 "StartBlock=%u EndBlock=%u\n";
446 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
447 " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
448 " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
449 " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
450 " VolReadTime=%" lld " VolWriteTime=%" lld;
452 "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";
453 struct VOLUME_CAT_INFO {
454 /* Media info for the current Volume */
455 uint32_t VolCatJobs; /* number of jobs on this Volume */
456 uint32_t VolCatFiles; /* Number of files */
457 uint32_t VolCatBlocks; /* Number of blocks */
458 uint64_t VolCatBytes; /* Number of bytes written */
459 uint32_t VolCatMounts; /* Number of mounts this volume */
460 uint32_t VolCatErrors; /* Number of errors this volume */
461 uint32_t VolCatWrites; /* Number of writes this volume */
462 uint32_t VolCatReads; /* Number of reads this volume */
463 uint64_t VolCatRBytes; /* Number of bytes read */
464 uint32_t VolCatRecycles; /* Number of recycles this volume */
465 int32_t Slot; /* Slot in changer */
466 bool InChanger; /* Set if vol in current magazine */
467 uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
468 uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
469 uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
470 uint64_t VolCatCapacityBytes; /* capacity estimate */
471 uint64_t VolReadTime; /* time spent reading */
472 uint64_t VolWriteTime; /* time spent writing this Volume */
473 char VolCatStatus[20]; /* Volume status */
474 char VolCatName[MAX_NAME_LENGTH]; /* Desired volume to mount */
476 struct VOLUME_CAT_INFO vol;
479 bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
480 printf("%s %d %lld\n", buf, val32, val64);
483 cnt = bsscanf(catreq, Create_job_media, &Job,
484 &FirstIndex, &LastIndex, &StartFile, &EndFile,
485 &StartBlock, &EndBlock);
486 printf("cnt=%d Job=%s\n", cnt, Job);
487 cnt = bsscanf(helloreq, hello, &Job);
488 printf("cnt=%d Agent=%s\n", cnt, Job);
490 cnt = bsscanf(media, OK_media,
492 &vol.VolCatJobs, &vol.VolCatFiles,
493 &vol.VolCatBlocks, &vol.VolCatBytes,
494 &vol.VolCatMounts, &vol.VolCatErrors,
495 &vol.VolCatWrites, &vol.VolCatMaxBytes,
496 &vol.VolCatCapacityBytes, vol.VolCatStatus,
497 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
498 &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
499 printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);