2 * scan.c -- scanning routines for Bacula
4 * Kern Sibbald, MM separated from util.c MMIII
9 Bacula® - The Network Backup Solution
11 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
13 The main author of Bacula is Kern Sibbald, with contributions from
14 many others, a complete list can be found in the file AUTHORS.
15 This program is Free Software; you can redistribute it and/or
16 modify it under the terms of version two of the GNU General Public
17 License as published by the Free Software Foundation plus additions
18 that are listed in the file LICENSE.
20 This program is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
30 Bacula® is a registered trademark of John Walker.
31 The licensor of Bacula is the Free Software Foundation Europe
32 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
33 Switzerland, email:ftf@fsfeurope.org.
39 #include "findlib/find.h"
41 /* Strip leading space from command line arguments */
42 void strip_leading_space(char *str)
45 while (B_ISSPACE(*p)) {
54 /* Strip any trailing junk from the command */
55 void strip_trailing_junk(char *cmd)
58 p = cmd + strlen(cmd) - 1;
60 /* strip trailing junk from command */
61 while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
65 /* Strip any trailing newline characters from the string */
66 void strip_trailing_newline(char *cmd)
69 p = cmd + strlen(cmd) - 1;
71 while ((p >= cmd) && (*p == '\n' || *p == '\r'))
75 /* Strip any trailing slashes from a directory path */
76 void strip_trailing_slashes(char *dir)
79 p = dir + strlen(dir) - 1;
81 /* strip trailing slashes */
82 while (p >= dir && IsPathSeparator(*p))
88 * Returns: 0 on failure (EOF)
90 * new address in passed parameter
92 bool skip_spaces(char **msg)
98 while (*p && B_ISSPACE(*p)) {
102 return *p ? true : false;
107 * Returns: 0 on failure (EOF)
109 * new address in passed parameter
111 bool skip_nonspaces(char **msg)
118 while (*p && !B_ISSPACE(*p)) {
122 return *p ? true : false;
125 /* folded search for string - case insensitive */
127 fstrsch(const char *a, const char *b) /* folded case search */
134 while (*s1) { /* do it the fast way */
135 if ((*s1++ | 0x20) != (*s2++ | 0x20))
136 return 0; /* failed */
138 while (*a) { /* do it over the correct slow way */
139 if (B_ISUPPER(c1 = *a)) {
140 c1 = tolower((int)c1);
142 if (B_ISUPPER(c2 = *b)) {
143 c2 = tolower((int)c2);
156 * Return next argument from command line. Note, this
157 * routine is destructive because it stored 0 at the end
159 * Called with pointer to pointer to command line. This
160 * pointer is updated to point to the remainder of the
163 * Returns pointer to next argument -- don't store the result
164 * in the pointer you passed as an argument ...
165 * The next argument is terminated by a space unless within
166 * quotes. Double quote characters (unless preceded by a \) are
170 char *next_arg(char **s)
173 bool in_quote = false;
175 /* skip past spaces to next arg */
176 for (p=*s; *p && B_ISSPACE(*p); ) {
179 Dmsg1(900, "Next arg=%s\n", p);
180 for (n = q = p; *p ; ) {
181 if (*p == '\\') { /* slash? */
182 p++; /* yes, skip it */
190 if (*p == '"') { /* start or end of quote */
192 in_quote = !in_quote; /* change state */
195 if (!in_quote && B_ISSPACE(*p)) { /* end of field */
203 Dmsg2(900, "End arg=%s next=%s\n", n, p);
208 * This routine parses the input command line.
209 * It makes a copy in args, then builds an
210 * argc, argk, argv list where:
212 * argc = count of arguments
213 * argk[i] = argument keyword (part preceding =)
214 * argv[i] = argument value (part after =)
216 * example: arg1 arg2=abc arg3=
226 int parse_args(POOLMEM *cmd, POOLMEM **args, int *argc,
227 char **argk, char **argv, int max_args)
231 parse_args_only(cmd, args, argc, argk, argv, max_args);
233 /* Separate keyword and value */
234 for (int i=0; i < *argc; i++) {
235 p = strchr(argk[i], '=');
237 *p++ = 0; /* terminate keyword and point to value */
238 if (strlen(p) > MAX_NAME_LENGTH-1) {
239 p[MAX_NAME_LENGTH-1] = 0; /* truncate to max len */
242 argv[i] = p; /* save ptr to value or NULL */
245 for (int i=0; i < *argc; i++) {
246 Pmsg3(000, "Arg %d: kw=%s val=%s\n", i, argk[i], argv[i]?argv[i]:"NULL");
254 * This routine parses the input command line.
255 * It makes a copy in args, then builds an
256 * argc, argk, but no argv (values).
257 * This routine is useful for scanning command lines where the data
258 * is a filename and no keywords are expected. If we scan a filename
259 * for keywords, any = in the filename will be interpreted as the
260 * end of a keyword, and this is not good.
262 * argc = count of arguments
263 * argk[i] = argument keyword (part preceding =)
266 * example: arg1 arg2=abc arg3=
276 int parse_args_only(POOLMEM *cmd, POOLMEM **args, int *argc,
277 char **argk, char **argv, int max_args)
281 pm_strcpy(args, cmd);
282 strip_trailing_junk(*args);
285 /* Pick up all arguments */
286 while (*argc < max_args) {
290 argv[(*argc)++] = NULL;
300 * Given a full filename, split it into its path
301 * and filename parts. They are returned in pool memory
302 * in the arguments provided.
304 void split_path_and_filename(const char *fname, POOLMEM **path, int *pnl,
305 POOLMEM **file, int *fnl)
309 int len = slen = strlen(fname);
312 * Find path without the filename.
313 * I.e. everything after the last / is a "filename".
314 * OK, maybe it is a directory name, but we treat it like
315 * a filename. If we don't find a / then the whole name
316 * must be a path name (e.g. c:).
319 /* "strip" any trailing slashes */
320 while (slen > 1 && IsPathSeparator(*f)) {
324 /* Walk back to last slash -- begin of filename */
325 while (slen > 0 && !IsPathSeparator(*f)) {
329 if (IsPathSeparator(*f)) { /* did we find a slash? */
330 f++; /* yes, point to filename */
331 } else { /* no, whole thing must be path name */
334 Dmsg2(200, "after strip len=%d f=%s\n", len, f);
335 *fnl = fname - f + len;
337 *file = check_pool_memory_size(*file, *fnl+1);
338 memcpy(*file, f, *fnl); /* copy filename */
344 *path = check_pool_memory_size(*path, *pnl+1);
345 memcpy(*path, fname, *pnl);
349 Dmsg2(200, "pnl=%d fnl=%d\n", *pnl, *fnl);
350 Dmsg3(200, "split fname=%s path=%s file=%s\n", fname, *path, *file);
354 * Extremely simple sscanf. Handles only %(u,d,ld,qd,qu,lu,lld,llu,c,nns)
356 const int BIG = 1000;
357 int bsscanf(const char *buf, const char *fmt, ...)
369 while (*fmt && !error) {
370 // Dmsg1(000, "fmt=%c\n", *fmt);
373 // Dmsg1(000, "Got %% nxt=%c\n", *fmt);
379 while (B_ISDIGIT(*buf)) {
380 value = B_TIMES10(value) + *buf++ - '0';
382 vp = (void *)va_arg(ap, void *);
383 // Dmsg2(000, "val=%lld at 0x%lx\n", value, (long unsigned)vp);
385 *((int *)vp) = (int)value;
387 *((uint32_t *)vp) = (uint32_t)value;
388 // Dmsg0(000, "Store 32 bit int\n");
390 *((uint64_t *)vp) = (uint64_t)value;
391 // Dmsg0(000, "Store 64 bit int\n");
397 // Dmsg0(000, "got l\n");
403 if (*fmt == 'd' || *fmt == 'u') {
406 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
411 if (*fmt == 'd' || *fmt == 'u') {
414 // Dmsg1(000, "fmt=%c !=d,u\n", *fmt);
418 // Dmsg1(000, "Store string max_len=%d\n", max_len);
419 cp = (char *)va_arg(ap, char *);
420 while (*buf && !B_ISSPACE(*buf) && max_len-- > 0) {
428 cp = (char *)va_arg(ap, char *);
440 while (B_ISDIGIT(*fmt)) {
441 max_len = B_TIMES10(max_len) + *fmt++ - '0';
443 // Dmsg1(000, "Default max_len=%d\n", max_len);
447 // Dmsg1(000, "Default c=%c\n", *fmt);
449 break; /* error: unknown format */
453 /* White space eats zero or more whitespace */
454 } else if (B_ISSPACE(*fmt)) {
456 while (B_ISSPACE(*buf)) {
459 /* Plain text must match */
460 } else if (*buf++ != *fmt++) {
461 // Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
467 // Dmsg2(000, "Error=%d count=%d\n", error, count);
475 int main(int argc, char *argv[])
480 uint32_t FirstIndex, LastIndex, StartFile, EndFile, StartBlock, EndBlock;
483 char *helloreq= "Hello *UserAgent* calling\n";
484 char *hello = "Hello %127s calling\n";
486 "CatReq Job=NightlySave.2004-06-11_19.11.32 CreateJobMedia FirstIndex=1 LastIndex=114 StartFile=0 EndFile=0 StartBlock=208 EndBlock=2903248";
487 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
488 "FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
489 "StartBlock=%u EndBlock=%u\n";
490 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
491 " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
492 " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
493 " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
494 " VolReadTime=%" lld " VolWriteTime=%" lld;
496 "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";
497 struct VOLUME_CAT_INFO {
498 /* Media info for the current Volume */
499 uint32_t VolCatJobs; /* number of jobs on this Volume */
500 uint32_t VolCatFiles; /* Number of files */
501 uint32_t VolCatBlocks; /* Number of blocks */
502 uint64_t VolCatBytes; /* Number of bytes written */
503 uint32_t VolCatMounts; /* Number of mounts this volume */
504 uint32_t VolCatErrors; /* Number of errors this volume */
505 uint32_t VolCatWrites; /* Number of writes this volume */
506 uint32_t VolCatReads; /* Number of reads this volume */
507 uint64_t VolCatRBytes; /* Number of bytes read */
508 uint32_t VolCatRecycles; /* Number of recycles this volume */
509 int32_t Slot; /* Slot in changer */
510 bool InChanger; /* Set if vol in current magazine */
511 uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
512 uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
513 uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
514 uint64_t VolCatCapacityBytes; /* capacity estimate */
515 uint64_t VolReadTime; /* time spent reading */
516 uint64_t VolWriteTime; /* time spent writing this Volume */
517 char VolCatStatus[20]; /* Volume status */
518 char VolCatName[MAX_NAME_LENGTH]; /* Desired volume to mount */
520 struct VOLUME_CAT_INFO vol;
523 bsscanf("Hello_world 123 1234", "%120s %ld %lld", buf, &val32, &val64);
524 printf("%s %d %lld\n", buf, val32, val64);
527 cnt = bsscanf(catreq, Create_job_media, &Job,
528 &FirstIndex, &LastIndex, &StartFile, &EndFile,
529 &StartBlock, &EndBlock);
530 printf("cnt=%d Job=%s\n", cnt, Job);
531 cnt = bsscanf(helloreq, hello, &Job);
532 printf("cnt=%d Agent=%s\n", cnt, Job);
534 cnt = bsscanf(media, OK_media,
536 &vol.VolCatJobs, &vol.VolCatFiles,
537 &vol.VolCatBlocks, &vol.VolCatBytes,
538 &vol.VolCatMounts, &vol.VolCatErrors,
539 &vol.VolCatWrites, &vol.VolCatMaxBytes,
540 &vol.VolCatCapacityBytes, vol.VolCatStatus,
541 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
542 &vol.InChanger, &vol.VolReadTime, &vol.VolWriteTime);
543 printf("cnt=%d Vol=%s\n", cnt, vol.VolCatName);