1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
32 */
33
34 #include <stdio.h>
35 #include <err.h>
36 #include <sys/types.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <ctype.h>
40 #include <assert.h>
41 #include <libcustr.h>
42 #include "cron.h"
43
44 #ifdef PARSETEST
45 #define xstrdup(x) strdup((x))
46 #endif
47
48 #define MAX_ELEMENTS 60
49
50 #define READNUMBER(x) \
51 do { \
52 (x) = (x) * 10 + (line[cursor] - '0'); \
53 if ((x) > MAX_ELEMENTS) { \
54 err = CFOUTOFBOUND; \
55 goto out; \
56 } \
57 } while (isdigit(line[++cursor]))
58
59 #define ADDELEMENT(x) \
60 do { \
61 if (eindex >= MAX_ELEMENTS) { \
62 err = CFEOVERFLOW; \
63 goto out; \
64 } \
65 elements[eindex++] = (x); \
66 } while (0)
67
68 /* A more restrictive version of isspace(3C) that only looks for space/tab */
69 #define ISSPACE(x) \
70 ((x) == ' ' || (x) == '\t')
71
72 cferror_t
next_field(uint_t lower,uint_t upper,char * line,int * cursorp,char ** ret)73 next_field(uint_t lower, uint_t upper, char *line, int *cursorp, char **ret)
74 {
75 uint_t elements[MAX_ELEMENTS];
76 uint_t eindex = 0, i;
77 int cursor = *cursorp;
78 cferror_t err = CFOK;
79
80 assert(upper - lower <= MAX_ELEMENTS);
81
82 if (ret != NULL)
83 *ret = NULL;
84
85 while (ISSPACE(line[cursor]))
86 cursor++;
87
88 if (line[cursor] == '\0') {
89 err = CFEOLN;
90 goto out;
91 }
92
93 for (;;) {
94 uint_t num = 0, num2 = 0, step = 0;
95
96 if (line[cursor] == '*') {
97 cursor++;
98
99 /* Short circuit for plain '*' */
100 if (ISSPACE(line[cursor])) {
101 if (ret != NULL)
102 *ret = xstrdup("*");
103 goto out;
104 }
105
106 /*
107 * '*' is only permitted alongside other elements if
108 * it has an associated step.
109 */
110
111 if (line[cursor] != '/') {
112 err = CFUNEXPECT;
113 goto out;
114 }
115
116 /* Treat it as a range covering all values */
117 num = lower;
118 num2 = upper;
119 } else {
120 if (!isdigit(line[cursor])) {
121 err = CFUNEXPECT;
122 goto out;
123 }
124
125 READNUMBER(num);
126
127 if (num < lower || num > upper) {
128 err = CFOUTOFBOUND;
129 goto out;
130 }
131
132 if (line[cursor] == '-') {
133 cursor++;
134 if (!isdigit(line[cursor])) {
135 err = CFUNEXPECT;
136 goto out;
137 }
138
139 READNUMBER(num2);
140
141 if (num2 < lower || num2 > upper) {
142 err = CFOUTOFBOUND;
143 goto out;
144 }
145 } else {
146 ADDELEMENT(num);
147 goto next;
148 }
149 }
150
151 /* Look for a step definition */
152 if (line[cursor] == '/') {
153 cursor++;
154 if (!isdigit(line[cursor])) {
155 err = CFUNEXPECT;
156 goto out;
157 }
158
159 READNUMBER(step);
160
161 if (step == 0) {
162 err = CFOUTOFBOUND;
163 goto out;
164 }
165 } else {
166 step = 1;
167 }
168
169 if (num <= num2) {
170 for (i = num; i <= num2; i += step) {
171 ADDELEMENT(i);
172 }
173 } else {
174 /* Wrap-around range */
175 for (i = num; i <= upper; i += step) {
176 ADDELEMENT(i);
177 }
178
179 i -= (upper - lower + 1);
180 for (; i <= num2; i += step) {
181 ADDELEMENT(i);
182 }
183 }
184
185 next:
186
187 if (line[cursor] != ',')
188 break;
189
190 cursor++;
191 }
192
193 if (line[cursor] == '\0') {
194 err = CFEOLN;
195 goto out;
196 }
197
198 if (!ISSPACE(line[cursor])) {
199 err = CFUNEXPECT;
200 goto out;
201 }
202
203 if (ret != NULL) {
204 custr_t *cs = NULL;
205
206 if (custr_alloc(&cs) != 0) {
207 err = CFENOMEM;
208 goto out;
209 }
210
211 for (i = 0; i < eindex; i++) {
212 if (custr_len(cs) > 0) {
213 if (custr_appendc(cs, ',') != 0) {
214 custr_free(cs);
215 err = CFENOMEM;
216 goto out;
217 }
218 }
219 if (custr_append_printf(cs, "%u", elements[i]) != 0) {
220 custr_free(cs);
221 err = CFENOMEM;
222 goto out;
223 }
224 }
225
226 if (custr_len(cs) != 0)
227 *ret = xstrdup(custr_cstr(cs));
228 custr_free(cs);
229 }
230
231 out:
232
233 *cursorp = cursor;
234
235 return (err);
236 }
237
238 #ifdef PARSETEST
239 int
main(int argc,char ** argv)240 main(int argc, char **argv)
241 {
242 int lower, upper, cursor = 0;
243 char *ret;
244
245 if (argc != 4)
246 errx(1, "<lower> <upper> <string>");
247
248 lower = atoi(argv[1]);
249 upper = atoi(argv[2]);
250
251 switch (next_field(lower, upper, argv[3], &cursor, &ret)) {
252 case CFOK:
253 (void) printf("%s\n", ret);
254 break;
255 case CFEOLN:
256 (void) printf("UnexpectedEOL\n");
257 break;
258 case CFUNEXPECT:
259 (void) printf("UnexpectedChar\n");
260 break;
261 case CFOUTOFBOUND:
262 (void) printf("OutOfBounds\n");
263 break;
264 case CFEOVERFLOW:
265 (void) printf("Overflow\n");
266 break;
267 case CFENOMEM:
268 (void) printf("OutOfMemory\n");
269 break;
270 default:
271 (void) printf("UnknownError\n");
272 break;
273 }
274
275 return (0);
276 }
277 #endif
278