1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <locale.h>
9 #include <stddef.h>
10 #include <limits.h>
11
12 #include <project.h>
13
14 #include <ctype.h>
15
16 #include "util.h"
17
18
19
20 #define BOSTR_REG_EXP "^"
21 #define EOSTR_REG_EXP "$"
22 #define FLTNM_REG_EXP "([[:digit:]]+(\\.[[:digit:]]+)?)"
23 #define MODIF_REG_EXP "([kmgtpeKMGTPE])?"
24 #define UNIT__REG_EXP "([bsBS])?"
25 #define TOKEN_REG_EXP "[[:alnum:]_./=+-]*"
26 #define VALUE_REG_EXP FLTNM_REG_EXP MODIF_REG_EXP UNIT__REG_EXP
27
28 #define TO_EXP(X) BOSTR_REG_EXP X EOSTR_REG_EXP
29 #define TOKEN_EXP TO_EXP(TOKEN_REG_EXP)
30 #define VALUE_EXP TO_EXP(VALUE_REG_EXP)
31
32 #define BYTES_SCALE 1
33 #define SCNDS_SCALE 2
34
35 char
36 *util_safe_strdup(char *str)
37 {
38 char *ptr;
39 if (str == NULL)
40 return (NULL);
41
42 if ((ptr = strdup(str)) == NULL) {
43 (void) fprintf(stderr, gettext("error allocating memory"));
44 exit(1);
45 }
46 return (ptr);
47 }
48
49 void *
50 util_safe_realloc(void *ptr, size_t sz)
51 {
52 if ((ptr = realloc(ptr, sz)) == NULL) {
53 (void) fprintf(stderr, gettext(
54 "error reallocating %d bytes of memory"), sz);
55 exit(1);
56 }
57 return (ptr);
58 }
59
60
61 void *
62 util_safe_malloc(size_t sz)
63 {
64 char *ptr;
65 if ((ptr = malloc(sz)) == NULL) {
66 (void) fprintf(stderr, gettext(
67 "error allocating %d bytes of memory\n"), sz);
68 exit(1);
69 }
70 return (ptr);
71 }
72
73 void *
74 util_safe_zmalloc(size_t sz)
75 {
76 return (memset(util_safe_malloc(sz), 0, sz));
77 }
78
79 void
80 util_print_errmsgs(lst_t *errlst)
81 {
82 char *errmsg;
83 while (!lst_is_empty(errlst)) {
84 errmsg = lst_at(errlst, 0);
85 (void) lst_remove(errlst, errmsg);
86 (void) fprintf(stderr, "%s\n", errmsg);
87 free(errmsg);
88 }
89 }
90
91
92 void
93 util_add_errmsg(lst_t *errlst, char *format, ...)
94 {
95 va_list args;
96 char *errmsg;
97
98 va_start(args, format);
99 if (vasprintf(&errmsg, format, args) < 0) {
100 va_end(args);
101 (void) fprintf(stderr, gettext(
102 "error allocating memory\n"));
103 exit(1);
104 }
105 va_end(args);
106 lst_insert_tail(errlst, errmsg);
107 }
108
109 char *
110 util_str_append(char *str, int nargs, ...)
111 {
112 va_list ap;
113 int i, len;
114 char *s;
115
116 if (str == NULL)
117 str = util_safe_zmalloc(1);
118
119 len = strlen(str) + 1;
120 va_start(ap, nargs);
121 for (i = 0; i < nargs; i++) {
122 s = va_arg(ap, char *);
123 len += strlen(s);
124 str = util_safe_realloc(str, len);
125 (void) strcat(str, s);
126 }
127 va_end(ap);
128 return (str);
129 }
130
131 char *
132 util_substr(regex_t *reg, regmatch_t *mat, char *str, int idx)
133 {
134 int mat_len;
135 char *ret;
136
137 if (idx < 0 || idx > reg->re_nsub)
138 return (NULL);
139
140 mat_len = mat[idx].rm_eo - mat[idx].rm_so;
141
142 ret = util_safe_malloc(mat_len + 1);
143 *ret = '\0';
144
145 (void) strlcpy(ret, str + mat[idx].rm_so, mat_len + 1);
146 return (ret);
147 }
148
149 typedef struct {
150 char unit;
151 uint64_t val;
152 } scl;
153
154 #define SCLS 7
155
156 int
157 util_scale(char *unit, int scale, uint64_t *res, lst_t *errlst)
158 {
159 int i;
160 scl *sc;
161 scl bscale[SCLS] = {
162 {'\0', 1ULL},
163 {'k', 1024ULL},
164 {'m', 1048576ULL},
165 {'g', 1073741824ULL},
166 {'t', 1099511627776ULL},
167 {'p', 1125899906842624ULL},
168 {'e', 1152921504606846976ULL},
169 };
170
171 scl oscale[SCLS] = {
172 {'\0', 1ULL},
173 {'k', 1000ULL},
174 {'m', 1000000ULL},
175 {'g', 1000000000ULL},
176 {'t', 1000000000000ULL},
177 {'p', 1000000000000000ULL},
178 {'e', 1000000000000000000ULL},
179 };
180
181 sc = (scale == BYTES_SCALE) ? bscale : oscale;
182
183 for (i = 0; i < SCLS; i++) {
184 if (tolower(*unit) == sc[i].unit) {
185 *res = sc[i].val;
186 return (0);
187 }
188 }
189
190 util_add_errmsg(errlst, gettext(
191 "Invalid scale: %d"), scale);
192 return (1);
193 }
194
195
196 int
197 util_val2num(char *value, int scale, lst_t *errlst, char **retnum,
198 char **retmod, char **retunit)
199 {
200 int ret = 1;
201 regex_t valueexp;
202 regmatch_t *mat;
203 int nmatch;
204 char *num, *modifier, *unit;
205 char *ptr;
206
207 uint64_t mul64;
208 long double dnum;
209
210 *retnum = *retmod = *retunit = NULL;
211
212 if (regcomp(&valueexp, VALUE_EXP, REG_EXTENDED) != 0) {
213 util_add_errmsg(errlst, gettext(
214 "Failed to compile regex: '%s'"), VALUE_EXP);
215 return (1);
216 }
217
218 nmatch = valueexp.re_nsub + 1;
219 mat = util_safe_malloc(nmatch * sizeof (regmatch_t));
220
221 if (regexec(&valueexp, value, nmatch, mat, 0) != 0) {
222 util_add_errmsg(errlst, gettext(
223 "Invalid value: '%s'"), value);
224 regfree(&valueexp);
225 return (1);
226 }
227 regfree(&valueexp);
228
229 num = util_substr(&valueexp, mat, value, 1);
230 modifier = util_substr(&valueexp, mat, value, 3);
231 unit = util_substr(&valueexp, mat, value, 4);
232
233 if ((num == NULL || modifier == NULL || unit == NULL) ||
234 (strlen(modifier) == 0 && strchr(num, '.') != NULL) ||
235 (util_scale(modifier, scale, &mul64, errlst) != 0) ||
236 (scale == BYTES_SCALE && *unit != '\0' && tolower(*unit) != 'b') ||
237 (scale == SCNDS_SCALE && *unit != '\0' && tolower(*unit) != 's') ||
238 (scale == COUNT_SCALE && *unit != '\0') ||
239 (scale == UNKWN_SCALE && *unit != '\0')) {
240 util_add_errmsg(errlst, gettext("Error near: \"%s\""),
241 value);
242 goto out;
243 }
244
245 dnum = strtold(num, &ptr);
246 if (dnum == 0 &&
247 (errno == EINVAL || errno == ERANGE) &&
248 *ptr != '\0') {
249 util_add_errmsg(errlst, gettext("Invalid value: \"%s\""),
250 value);
251 goto out;
252 }
253
254 if (UINT64_MAX / mul64 <= dnum) {
255 util_add_errmsg(errlst, gettext("Too big value: \"%s\""),
256 value);
257 goto out;
258 }
259
260 if (asprintf(retnum, "%llu",
261 (unsigned long long)(mul64 * dnum)) == -1) {
262 goto out;
263 }
264
265 free(num);
266 *retmod = modifier;
267 *retunit = unit;
268 return (0);
269 out:
270 free(num); free(modifier); free(unit);
271 return (ret);
272 }
273
274 void
275 util_free_tokens(char **tokens)
276 {
277 char *token;
278 while ((token = *tokens++) != NULL) {
279 free(token);
280 }
281 }
282
283 char **
284 util_tokenize(char *values, lst_t *errlst)
285 {
286 char *token, *t;
287 char *v;
288 regex_t tokenexp;
289 char **ctoken, **tokens = NULL;
290
291 if (regcomp(&tokenexp, TOKEN_EXP, REG_EXTENDED | REG_NOSUB) != 0)
292 return (tokens);
293
294 /* Assume each character will be a token + NULL terminating value. */
295 ctoken = tokens = util_safe_malloc(
296 (strlen(values) + 1) * sizeof (char *));
297 token = util_safe_malloc(strlen(values) + 1);
298
299 v = values;
300 *ctoken = NULL;
301 while (v < (values + strlen(values))) {
302
303 /* get the next token */
304 t = token;
305 while ((*t = *v++) != '\0') {
306 if (*t == '(' || *t == ')' || *t == ','||
307 *v == '(' || *v == ')' || *v == ',') {
308 *++t = '\0';
309 break;
310 }
311 t++;
312 }
313
314 if (strcmp(token, "(") != 0 && strcmp(token, ")") != 0 &&
315 strcmp(token, ",") != 0) {
316 if (regexec(&tokenexp, token, 0, NULL, 0) != 0) {
317 util_add_errmsg(errlst, gettext(
318 "Invalid Character at or near \"%s\""),
319 token);
320 util_free_tokens(tokens);
321 UTIL_FREE_SNULL(tokens);
322 goto out1;
323 }
324 }
325 *ctoken++ = util_safe_strdup(token);
326 *ctoken = NULL;
327 }
328
329 out1:
330 free(token);
331 regfree(&tokenexp);
332 return (tokens);
333 }