1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2016-2016 Planets Communications B.V.
6    Copyright (C) 2016-2016 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * scan.c -- scanning routines for BAREOS
25  *
26  * Kern Sibbald, MM  separated from util.c MMIII
27  */
28 
29 #include "include/bareos.h"
30 #include "jcr.h"
31 
32 /*
33  * Strip leading space from command line arguments
34  */
StripLeadingSpace(char * str)35 void StripLeadingSpace(char* str)
36 {
37   char* p = str;
38 
39   while (B_ISSPACE(*p)) { p++; }
40   if (p != str) { bstrinlinecpy(str, p); }
41 }
42 
43 /*
44  * Strip any trailing junk from the command
45  */
StripTrailingJunk(char * cmd)46 void StripTrailingJunk(char* cmd)
47 {
48   char* p;
49 
50   /*
51    * Strip trailing junk from command
52    */
53   p = cmd + strlen(cmd) - 1;
54   while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' ')) { *p-- = 0; }
55 }
56 
57 /*
58  * Strip any trailing newline characters from the string
59  */
StripTrailingNewline(char * cmd)60 void StripTrailingNewline(char* cmd)
61 {
62   char* p;
63 
64   p = cmd + strlen(cmd) - 1;
65   while ((p >= cmd) && (*p == '\n' || *p == '\r')) { *p-- = 0; }
66 }
67 
68 /*
69  * Strip any trailing slashes from a directory path
70  */
StripTrailingSlashes(char * dir)71 void StripTrailingSlashes(char* dir)
72 {
73   char* p;
74 
75   /*
76    * Strip trailing slashes
77    */
78   p = dir + strlen(dir) - 1;
79   while (p >= dir && IsPathSeparator(*p)) { *p-- = 0; }
80 }
81 
82 /*
83  * Skip spaces
84  *  Returns: 0 on failure (EOF)
85  *           1 on success
86  *           new address in passed parameter
87  */
SkipSpaces(char ** msg)88 bool SkipSpaces(char** msg)
89 {
90   char* p = *msg;
91   if (!p) { return false; }
92   while (*p && B_ISSPACE(*p)) { p++; }
93   *msg = p;
94   return *p ? true : false;
95 }
96 
97 /*
98  * Skip nonspaces
99  *  Returns: 0 on failure (EOF)
100  *           1 on success
101  *           new address in passed parameter
102  */
SkipNonspaces(char ** msg)103 bool SkipNonspaces(char** msg)
104 {
105   char* p = *msg;
106 
107   if (!p) { return false; }
108   while (*p && !B_ISSPACE(*p)) { p++; }
109   *msg = p;
110   return *p ? true : false;
111 }
112 
113 /*
114  * Folded search for string - case insensitive
115  */
fstrsch(const char * a,const char * b)116 int fstrsch(const char* a, const char* b) /* folded case search */
117 {
118   const char *s1, *s2;
119   char c1, c2;
120 
121   s1 = a;
122   s2 = b;
123   while (*s1) {                                     /* do it the fast way */
124     if ((*s1++ | 0x20) != (*s2++ | 0x20)) return 0; /* failed */
125   }
126   while (*a) { /* do it over the correct slow way */
127     if (B_ISUPPER(c1 = *a)) { c1 = tolower((int)c1); }
128     if (B_ISUPPER(c2 = *b)) { c2 = tolower((int)c2); }
129     if (c1 != c2) { return 0; }
130     a++;
131     b++;
132   }
133   return 1;
134 }
135 
136 /*
137  * Return next argument from command line.  Note, this
138  *   routine is destructive because it stored 0 at the end
139  *   of each argument.
140  * Called with pointer to pointer to command line. This
141  *   pointer is updated to point to the remainder of the
142  *   command line.
143  *
144  * Returns pointer to next argument -- don't store the result
145  *   in the pointer you passed as an argument ...
146  *   The next argument is terminated by a space unless within
147  *   quotes. Double quote characters (unless preceded by a \) are
148  *   stripped.
149  *
150  */
next_arg(char ** s)151 char* next_arg(char** s)
152 {
153   char *p, *q, *n;
154   bool in_quote = false;
155 
156   /* skip past spaces to next arg */
157   for (p = *s; *p && B_ISSPACE(*p);) { p++; }
158   Dmsg1(900, "Next arg=%s\n", p);
159   for (n = q = p; *p;) {
160     if (*p == '\\') { /* slash? */
161       p++;            /* yes, skip it */
162       if (*p) {
163         *q++ = *p++;
164       } else {
165         *q++ = *p;
166       }
167       continue;
168     }
169     if (*p == '"') { /* start or end of quote */
170       p++;
171       in_quote = !in_quote; /* change state */
172       continue;
173     }
174     if (!in_quote && B_ISSPACE(*p)) { /* end of field */
175       p++;
176       break;
177     }
178     *q++ = *p++;
179   }
180   *q = 0;
181   *s = p;
182   Dmsg2(900, "End arg=%s next=%s\n", n, p);
183   return n;
184 }
185 
186 /*
187  * This routine parses the input command line.
188  * It makes a copy in args, then builds an
189  *  argc, argk, argv list where:
190  *
191  *  argc = count of arguments
192  *  argk[i] = argument keyword (part preceding =)
193  *  argv[i] = argument value (part after =)
194  *
195  *  example:  arg1 arg2=abc arg3=
196  *
197  *  argc = c
198  *  argk[0] = arg1
199  *  argv[0] = NULL
200  *  argk[1] = arg2
201  *  argv[1] = abc
202  *  argk[2] = arg3
203  *  argv[2] =
204  */
ParseArgs(POOLMEM * cmd,POOLMEM * & args,int * argc,char ** argk,char ** argv,int max_args)205 int ParseArgs(POOLMEM* cmd,
206               POOLMEM*& args,
207               int* argc,
208               char** argk,
209               char** argv,
210               int max_args)
211 {
212   char* p;
213 
214   ParseArgsOnly(cmd, args, argc, argk, argv, max_args);
215 
216   /* Separate keyword and value */
217   for (int i = 0; i < *argc; i++) {
218     p = strchr(argk[i], '=');
219     if (p) { *p++ = 0; /* Terminate keyword and point to value */ }
220     argv[i] = p; /* save ptr to value or NULL */
221   }
222   return 1;
223 }
224 
225 
226 /*
227  * This routine parses the input command line.
228  *   It makes a copy in args, then builds an
229  *   argc, argk, but no argv (values).
230  *   This routine is useful for scanning command lines where the data
231  *   is a filename and no keywords are expected.  If we scan a filename
232  *   for keywords, any = in the filename will be interpreted as the
233  *   end of a keyword, and this is not good.
234  *
235  *  argc = count of arguments
236  *  argk[i] = argument keyword (part preceding =)
237  *  argv[i] = NULL
238  *
239  *  example:  arg1 arg2=abc arg3=
240  *
241  *  argc = c
242  *  argk[0] = arg1
243  *  argv[0] = NULL
244  *  argk[1] = arg2=abc
245  *  argv[1] = NULL
246  *  argk[2] = arg3
247  *  argv[2] =
248  */
ParseArgsOnly(POOLMEM * cmd,POOLMEM * & args,int * argc,char ** argk,char ** argv,int max_args)249 int ParseArgsOnly(POOLMEM* cmd,
250                   POOLMEM*& args,
251                   int* argc,
252                   char** argk,
253                   char** argv,
254                   int max_args)
255 {
256   char *p, *n;
257 
258   PmStrcpy(args, cmd);
259   StripTrailingJunk(args);
260   p = args;
261   *argc = 0;
262   /*
263    * Pick up all arguments
264    */
265   while (*argc < max_args) {
266     n = next_arg(&p);
267     if (*n) {
268       argk[*argc] = n;
269       argv[(*argc)++] = NULL;
270     } else {
271       break;
272     }
273   }
274   return 1;
275 }
276 
277 /*
278  * Given a full filename, split it into its path and filename parts.
279  * They are returned in pool memory in the arguments provided.
280  */
SplitPathAndFilename(const char * fname,POOLMEM * & path,int * pnl,POOLMEM * & file,int * fnl)281 void SplitPathAndFilename(const char* fname,
282                           POOLMEM*& path,
283                           int* pnl,
284                           POOLMEM*& file,
285                           int* fnl)
286 {
287   const char* f;
288   int slen;
289   int len = slen = strlen(fname);
290 
291   /*
292    * Find path without the filename.
293    * I.e. everything after the last / is a "filename".
294    * OK, maybe it is a directory name, but we treat it like
295    * a filename. If we don't find a / then the whole name
296    * must be a path name (e.g. c:).
297    */
298   f = fname + len - 1;
299   /* "strip" any trailing slashes */
300   while (slen > 1 && IsPathSeparator(*f)) {
301     slen--;
302     f--;
303   }
304   /* Walk back to last slash -- begin of filename */
305   while (slen > 0 && !IsPathSeparator(*f)) {
306     slen--;
307     f--;
308   }
309   if (IsPathSeparator(*f)) { /* did we find a slash? */
310     f++;                     /* yes, point to filename */
311   } else {                   /* no, whole thing must be path name */
312     f = fname;
313   }
314   Dmsg2(200, "after strip len=%d f=%s\n", len, f);
315   *fnl = fname - f + len;
316   if (*fnl > 0) {
317     file = CheckPoolMemorySize(file, *fnl + 1);
318     memcpy(file, f, *fnl); /* copy filename */
319   }
320   file[*fnl] = '\0';
321 
322   *pnl = f - fname;
323   if (*pnl > 0) {
324     path = CheckPoolMemorySize(path, *pnl + 1);
325     memcpy(path, fname, *pnl);
326   }
327   path[*pnl] = '\0';
328 
329   Dmsg2(200, "pnl=%d fnl=%d\n", *pnl, *fnl);
330   Dmsg3(200, "split fname=%s path=%s file=%s\n", fname, path, file);
331 }
332 
333 /*
334  * Extremely simple sscanf. Handles only %(u,d,hu,hd,ld,qd,qu,lu,lld,llu,c,nns)
335  */
336 const int BIG = 1000;
bsscanf(const char * buf,const char * fmt,...)337 int bsscanf(const char* buf, const char* fmt, ...)
338 {
339   va_list ap;
340   int count = 0;
341   void* vp;
342   char* cp;
343   int l = 0;
344   int h = 0;
345   int max_len = BIG;
346   uint64_t value;
347   bool error = false;
348   bool negative;
349 
350   va_start(ap, fmt);
351   while (*fmt && !error) {
352     if (*fmt == '%') {
353       fmt++;
354     switch_top:
355       switch (*fmt++) {
356         case 'u':
357           value = 0;
358           while (B_ISDIGIT(*buf)) { value = B_TIMES10(value) + *buf++ - '0'; }
359           vp = (void*)va_arg(ap, void*);
360           if (h == 1) {
361             *((uint16_t*)vp) = (int16_t)value;
362           } else if (l == 0) {
363             *((unsigned int*)vp) = (unsigned int)value;
364           } else if (l == 1) {
365             *((uint32_t*)vp) = (uint32_t)value;
366           } else {
367             *((uint64_t*)vp) = (uint64_t)value;
368           }
369           count++;
370           h = 0;
371           l = 0;
372           break;
373         case 'd':
374           value = 0;
375           if (*buf == '-') {
376             negative = true;
377             buf++;
378           } else {
379             negative = false;
380           }
381           while (B_ISDIGIT(*buf)) { value = B_TIMES10(value) + *buf++ - '0'; }
382           if (negative) { value = -value; }
383           vp = (void*)va_arg(ap, void*);
384           if (h == 1) {
385             *((int16_t*)vp) = (int16_t)value;
386           } else if (l == 0) {
387             *((int*)vp) = (int)value;
388           } else if (l == 1) {
389             *((int32_t*)vp) = (int32_t)value;
390           } else {
391             *((int64_t*)vp) = (int64_t)value;
392           }
393           count++;
394           h = 0;
395           l = 0;
396           break;
397         case 'h':
398           h = 1;
399           if (*fmt == 'd' || *fmt == 'u') { goto switch_top; }
400           error = true;
401           break;
402         case 'l':
403           l = 1;
404           if (*fmt == 'l') {
405             l++;
406             fmt++;
407           }
408           if (*fmt == 'd' || *fmt == 'u') { goto switch_top; }
409           error = true;
410           break;
411         case 'q':
412           l = 2;
413           if (*fmt == 'd' || *fmt == 'u') { goto switch_top; }
414           error = true;
415           break;
416         case 's':
417           cp = (char*)va_arg(ap, char*);
418           while (*buf && !B_ISSPACE(*buf) && max_len-- > 0) { *cp++ = *buf++; }
419           *cp = 0;
420           count++;
421           max_len = BIG;
422           break;
423         case 'c':
424           cp = (char*)va_arg(ap, char*);
425           *cp = *buf++;
426           count++;
427           break;
428         case '%':
429           if (*buf++ != '%') { error = true; }
430           break;
431         default:
432           fmt--;
433           max_len = 0;
434           while (B_ISDIGIT(*fmt)) {
435             max_len = B_TIMES10(max_len) + *fmt++ - '0';
436           }
437           if (*fmt == 's') { goto switch_top; }
438           error = true;
439           break; /* error: unknown format */
440       }
441       continue;
442 
443       /* White space eats zero or more whitespace */
444     } else if (B_ISSPACE(*fmt)) {
445       fmt++;
446       while (B_ISSPACE(*buf)) { buf++; }
447       /* Plain text must match */
448     } else if (*buf++ != *fmt++) {
449       //       Dmsg2(000, "Mismatch buf=%c fmt=%c\n", *--buf, *--fmt);
450       error = true;
451       break;
452     }
453   }
454   va_end(ap);
455   // Dmsg2(000, "Error=%d count=%d\n", error, count);
456   if (error) { count = -1; }
457   return count;
458 }
459