1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * Kern Sibbald, January MMXII
21 *
22 * Selection list. A string of integers separated by commas
23 * representing items selected. Ranges of the form nn-mm
24 * are also permitted.
25 */
26
27 #include "bacula.h"
28
29 /*
30 * Returns next item
31 * error if returns -1 and errmsg set
32 * end of items if returns -1 and errmsg NULL
33 */
next()34 int64_t sellist::next()
35 {
36 errmsg = NULL;
37 if (beg <= end) { /* scan done? */
38 //printf("Return %lld\n", beg);
39 return beg++;
40 }
41 if (e == NULL) {
42 goto bail_out; /* nothing to scan */
43 }
44 /*
45 * As we walk the list, we set EOF in
46 * the end of the next item to ease scanning,
47 * but save and then restore the character.
48 */
49 for (p=e; p && *p; p=e) {
50 esave = hsave = 0;
51 /* Check for list */
52 e = strpbrk(p, ", ");
53 if (e) { /* have list */
54 esave = *e;
55 *e++ = 0;
56 }
57 /* Check for range */
58 h = strchr(p, '-'); /* range? */
59 if (h == p) {
60 errmsg = _("Negative numbers not permitted.\n");
61 goto bail_out;
62 }
63 if (h) { /* have range */
64 hsave = *h;
65 *h++ = 0;
66 if (!is_an_integer(h)) {
67 errmsg = _("Range end is not integer.\n");
68 goto bail_out;
69 }
70 skip_spaces(&p);
71 if (!is_an_integer(p)) {
72 errmsg = _("Range start is not an integer.\n");
73 goto bail_out;
74 }
75 beg = str_to_int64(p);
76 end = str_to_int64(h);
77 //printf("beg=%lld end=%lld\n", beg, end);
78 if (end <= beg) {
79 errmsg = _("Range end not bigger than start.\n");
80 goto bail_out;
81 }
82 } else { /* not list, not range */
83 skip_spaces(&p);
84 /* Check for abort (.) */
85 if (*p == '.') {
86 errmsg = _("User cancel requested.\n");
87 goto bail_out;
88 }
89 /* Check for all keyword */
90 if (strncasecmp(p, "all", 3) == 0) {
91 all = true;
92 errmsg = NULL;
93 //printf("Return 0 i.e. all\n");
94 return 0;
95 }
96 if (!is_an_integer(p)) {
97 errmsg = _("Input value is not an integer.\n");
98 goto bail_out;
99 }
100 beg = end = str_to_int64(p);
101 }
102 if (esave) {
103 *(e-1) = esave;
104 }
105 if (hsave) {
106 *(h-1) = hsave;
107 }
108 if (beg <= 0 || end <= 0) {
109 errmsg = _("Selection items must be be greater than zero.\n");
110 goto bail_out;
111 }
112 if (beg <= end) {
113 //printf("Return %lld\n", beg);
114 return beg++;
115 }
116 }
117 //printf("Rtn=-1. End of items\n");
118 /* End of items */
119 begin();
120 e = NULL;
121 return -1; /* No error */
122
123 bail_out:
124 if (errmsg) {
125 //printf("Bail out rtn=-1. p=%c err=%s\n", *p, errmsg);
126 } else {
127 //printf("Rtn=-1. End of items\n");
128 }
129 e = NULL;
130 return -1; /* Error, errmsg set */
131 }
132
133
134 /*
135 * Set selection string and optionally scan it
136 * returns false on error in string
137 * returns true if OK
138 */
139 bool sellist::set_string(const char *string, bool scan=true)
140 {
141 bool ok = true;
142 /*
143 * Copy string, because we write into it,
144 * then scan through it once to find any
145 * errors.
146 */
147 if (expanded) {
148 free(expanded);
149 expanded = NULL;
150 }
151 if (str) {
152 free(str);
153 }
154 str = bstrdup(string);
155 begin();
156 num_items = 0;
157 if (scan) {
158 while (next() >= 0) {
159 num_items++;
160 }
161 ok = get_errmsg() == NULL;
162 }
163 if (ok) {
164 begin();
165 } else {
166 e = NULL;
167 }
168 return ok;
169 }
170
171 /*
172 * Get the expanded list of values separated by commas,
173 * useful for SQL queries
174 */
get_expanded_list()175 char *sellist::get_expanded_list()
176 {
177 int32_t expandedsize = 512;
178 int32_t len;
179 int64_t val;
180 char *p, *tmp;
181 char ed1[50];
182
183 if (!expanded) {
184 p = expanded = (char *)malloc(expandedsize * sizeof(char));
185 *p = 0;
186
187 while ((val = next()) >= 0) {
188 edit_int64(val, ed1);
189 len = strlen(ed1);
190
191 /* Alloc more space if needed */
192 if ((p + len + 1) > (expanded + expandedsize)) {
193 expandedsize = expandedsize * 2;
194
195 tmp = (char *) realloc(expanded, expandedsize);
196
197 /* Compute new addresses for p and expanded */
198 p = tmp + (p - expanded);
199 expanded = tmp;
200 }
201
202 /* If not at the begining of the string, add a "," */
203 if (p != expanded) {
204 strcpy(p, ",");
205 p++;
206 }
207
208 strcpy(p, ed1);
209 p += len;
210 }
211 }
212 return expanded;
213 }
214
215 #ifndef TEST_PROGRAM
216 #define TEST_PROGRAM_A
217 #endif
218
219 #ifdef TEST_PROGRAM
220 #include "unittests.h"
221
222 struct test {
223 const int nr;
224 const char *sinp;
225 const char *sout;
226 const bool res;
227 };
228
229 static struct test tests[] = {
230 { 1, "1,70", "1,70", true, },
231 { 2, "1", "1", true, },
232 { 3, "256", "256", true, },
233 { 4, "1-5", "1,2,3,4,5", true, },
234 { 5, "1-5,7", "1,2,3,4,5,7", true, },
235 { 6, "1 10 20 30", "1,10,20,30", true, },
236 { 7, "1-5,7,20 21", "1,2,3,4,5,7,20,21", true, },
237 { 8, "all", "0", true, },
238 { 9, "12a", "", false, },
239 {10, "12-11", "", false, },
240 {11, "12-13a", "", false, },
241 {12, "a123", "", false, },
242 {13, "1 3", "", false, },
243 {0, "dummy", "dummy", false, },
244 };
245
246 #define ntests ((int)(sizeof(tests)/sizeof(struct test)))
247 #define MSGLEN 80
248
main()249 int main()
250 {
251 Unittests sellist_test("sellist_test");
252 const char *msg;
253 sellist sl;
254 char buf[MSGLEN];
255 bool check_rc;
256
257 for (int i = 0; i < ntests; i++) {
258 if (tests[i].nr > 0){
259 snprintf(buf, MSGLEN, "Checking test: %d - %s", tests[i].nr, tests[i].sinp);
260 check_rc = sl.set_string(tests[i].sinp, true);
261 msg = sl.get_expanded_list();
262 ok(check_rc == tests[i].res && strcmp(msg, tests[i].sout) == 0, buf);
263 }
264 }
265
266 return report();
267 }
268 #endif /* TEST_PROGRAM */
269