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