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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include "volume_string.h"
30
31 #include <ctype.h>
32 #include <errno.h>
33 #include <libintl.h>
34 #include <math.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include "volume_error.h"
40 #include "volume_output.h"
41
42 /*
43 * ******************************************************************
44 *
45 * Function prototypes
46 *
47 * ******************************************************************
48 */
49
50 static void *append_to_pointer_array(void **array, void *pointer);
51
52 /*
53 * ******************************************************************
54 *
55 * Data
56 *
57 * ******************************************************************
58 */
59
60 /* All-inclusive valid size units */
61 units_t universal_units[] = {
62 {"BLOCKS", BYTES_PER_BLOCK},
63 {"KB", BYTES_PER_KILOBYTE},
64 {"MB", BYTES_PER_MEGABYTE},
65 {"GB", BYTES_PER_GIGABYTE},
66 {"TB", BYTES_PER_TERABYTE},
67 {NULL, 0}
68 };
69
70 /*
71 * ******************************************************************
72 *
73 * External functions
74 *
75 * ******************************************************************
76 */
77
78 /*
79 * Concatenates a list of strings. The result must be free()d.
80 *
81 * @param numargs
82 * The number of strings to concatenate.
83 *
84 * @param ...
85 * The strings (type char *) to concatenate.
86 *
87 * @return the concatenated string
88 * if succesful
89 *
90 * @return NULL
91 * if memory could not be allocated
92 */
93 char *
stralloccat(int numargs,...)94 stralloccat(
95 int numargs,
96 ...)
97 {
98 va_list vl;
99 int i;
100 int len = 1;
101 char *cat;
102
103 /* Determine length of concatenated string */
104 va_start(vl, numargs);
105 for (i = 0; i < numargs; i++) {
106 char *str = va_arg(vl, char *);
107 if (str != NULL) {
108 len += strlen(str);
109 }
110 }
111 va_end(vl);
112
113 /* Allocate memory for concatenation plus a trailing NULL */
114 cat = (char *)calloc(1, len * sizeof (char));
115
116 if (cat == NULL) {
117 return (NULL);
118 }
119
120 /* Concatenate strings */
121 va_start(vl, numargs);
122 for (i = 0; i < numargs; i++) {
123 char *str = va_arg(vl, char *);
124 if (str != NULL) {
125 strcat(cat, str);
126 }
127 }
128 va_end(vl);
129
130 return (cat);
131 }
132
133 /*
134 * Convert the given string to a uint16_t, verifying that the value
135 * does not exceed the lower or upper bounds of a uint16_t.
136 *
137 * @param str
138 * the string to convert
139 *
140 * @param num
141 * the addr of the uint16_t
142 *
143 * @return 0
144 * if the given string was converted to a uint16_t
145 *
146 * @return -1
147 * if the string could could not be converted to a number
148 *
149 * @return -2
150 * if the converted number exceeds the lower or upper
151 * bounds of a uint16_t
152 */
153 int
str_to_uint16(char * str,uint16_t * num)154 str_to_uint16(
155 char *str,
156 uint16_t *num)
157 {
158 long long lnum;
159 int error = 0;
160
161 /* Convert string to long long */
162 if (sscanf(str, "%lld", &lnum) != 1) {
163 error = -1;
164 } else {
165
166 /*
167 * Verify that the long long value does not exceed the
168 * lower or upper bounds of a uint16_t
169 */
170
171 /* Maximum value of uint16_t */
172 uint16_t max = (uint16_t)~0ULL;
173
174 if (lnum < 0 || lnum > max) {
175 error = -2;
176 } else {
177 *num = lnum;
178 }
179 }
180
181 return (error);
182 }
183
184 /*
185 * Converts the given long long into a string. This string must be
186 * freed.
187 *
188 * @param num
189 * the long long to convert
190 *
191 * @param str
192 * the addr of the string
193 *
194 * @return 0
195 * if successful
196 *
197 * @return ENOMEM
198 * if the physical limits of the system are exceeded by
199 * size bytes of memory which cannot be allocated
200 *
201 * @return EAGAIN
202 * if there is not enough memory available to allocate
203 * size bytes of memory
204 */
205 int
ll_to_str(long long num,char ** str)206 ll_to_str(
207 long long num,
208 char **str)
209 {
210 int error = 0;
211
212 /* Allocate memory for the string */
213 if ((*str = calloc(1, LONG_LONG_STR_SIZE * sizeof (char))) == NULL) {
214 error = errno;
215 } else {
216 /* Convert the integer to a string */
217 snprintf(*str, LONG_LONG_STR_SIZE, "%lld", num);
218 }
219
220 return (error);
221 }
222
223 /*
224 * Convert a size specification to bytes.
225 *
226 * @param str
227 * a size specification strings of the form
228 * <value><units>, where valid <units> are specified by
229 * the units argument and <value> is the (floating-point)
230 * multiplier of the units
231 *
232 * @param bytes
233 * RETURN: the result of converting the given size string
234 * to bytes
235 *
236 * @return 0
237 * if successful
238 *
239 * @return non-zero
240 * if an error occurred. Use get_error_string() to
241 * retrieve the associated error message.
242 */
243 int
sizestr_to_bytes(char * str,uint64_t * bytes,units_t units[])244 sizestr_to_bytes(
245 char *str,
246 uint64_t *bytes,
247 units_t units[])
248 {
249 char *unit_str;
250 long double d;
251 int error = 0;
252 int i;
253
254 /* Convert <value> string to double */
255 if ((d = strtod(str, &unit_str)) == 0) {
256 volume_set_error(gettext("invalid size string: %s"), str);
257 error = -1;
258 } else {
259
260 /* Trim leading white space */
261 while (isspace(*unit_str) != 0) {
262 ++unit_str;
263 }
264
265 /* Convert to bytes based on <units> */
266 for (i = 0; units[i].unit_str != NULL; i++) {
267 if (strcasecmp(unit_str, units[i].unit_str) == 0) {
268 d *= units[i].bytes_per_unit;
269 break;
270 }
271 }
272
273 /* Was a valid unit string found? */
274 if (units[i].unit_str == NULL) {
275 volume_set_error(
276 gettext("missing or invalid units indicator in size: %s"),
277 str);
278 error = -1;
279 }
280 }
281
282 if (error) {
283 *bytes = 0;
284 } else {
285 *bytes = (uint64_t)d;
286 oprintf(OUTPUT_DEBUG,
287 gettext("converted \"%s\" to %llu bytes\n"), str, *bytes);
288 }
289
290 return (error);
291 }
292
293 /*
294 * Convert bytes to a size specification string.
295 *
296 * @param bytes
297 * the number of bytes
298 *
299 * @param str
300 * RETURN: a size specification strings of the form
301 * <value><units>, where valid <units> are specified by
302 * the units argument and <value> is the (floating-point)
303 * multiplier of the units. This string must be freed.
304 *
305 * @return 0
306 * if successful
307 *
308 * @return non-zero
309 * if an error occurred. Use get_error_string() to
310 * retrieve the associated error message.
311 */
312 int
bytes_to_sizestr(uint64_t bytes,char ** str,units_t units[],boolean_t round)313 bytes_to_sizestr(
314 uint64_t bytes,
315 char **str,
316 units_t units[],
317 boolean_t round)
318 {
319 int i, len, error = 0;
320 double value;
321 const char *format;
322 units_t use_units = units[0];
323
324 /* Determine the units to use */
325 for (i = 0; units[i].unit_str != NULL; i++) {
326 if (bytes >= units[i].bytes_per_unit) {
327 use_units = units[i];
328 }
329 }
330
331 value = ((long double)bytes / use_units.bytes_per_unit);
332
333 /* Length of string plus trailing NULL */
334 len = LONG_LONG_STR_SIZE + strlen(use_units.unit_str) + 1;
335
336 if (round) {
337 value = floor(value + 0.5F);
338 format = "%.0f%s";
339 } else {
340 format = "%.2f%s";
341 }
342
343 /* Append units to string */
344 *str = calloc(1, len * sizeof (char));
345 if (*str == NULL) {
346 error = errno;
347 } else {
348 snprintf(*str, len, format, value, use_units.unit_str);
349 }
350
351 return (error);
352 }
353
354 /*
355 * Appends a copy of the given string to the given string array,
356 * ensuring that the last element in the array is NULL. This array
357 * must be freed via free_string_array.
358 *
359 * Note when an error occurs and NULL is returned, array is not freed.
360 * Subsequently callers should save a pointer to the original array
361 * until success is verified.
362 *
363 * @param array
364 * the array to append to, or NULL to create a new array
365 *
366 * @param str
367 * the string to copy and add to the array
368 *
369 * @return a pointer to the realloc'd (and possibly moved) array
370 * if succesful
371 *
372 * @return NULL
373 * if unsuccesful
374 */
375 char **
append_to_string_array(char ** array,char * str)376 append_to_string_array(
377 char **array,
378 char *str)
379 {
380 char *copy = strdup(str);
381
382 if (copy == NULL) {
383 return (NULL);
384 }
385
386 return ((char **)append_to_pointer_array((void **)array, copy));
387 }
388
389 /*
390 * Frees each element of the given string array, then frees the array
391 * itself.
392 *
393 * @param array
394 * a NULL-terminated string array
395 */
396 void
free_string_array(char ** array)397 free_string_array(
398 char **array)
399 {
400 int i;
401
402 /* Free each available element */
403 for (i = 0; array[i] != NULL; i++) {
404 free(array[i]);
405 }
406
407 /* Free the array itself */
408 free((void *)array);
409 }
410
411 /*
412 * ******************************************************************
413 *
414 * Static functions
415 *
416 * ******************************************************************
417 */
418
419 /*
420 * Appends the given pointer to the given pointer array, ensuring that
421 * the last element in the array is NULL.
422 *
423 * Note when an error occurs and NULL is returned, array is not freed.
424 * Subsequently callers should save a pointer to the original array
425 * until success is verified.
426 *
427 * @param array
428 * the array to append to, or NULL to create a new array
429 *
430 * @param pointer
431 * the pointer to add to the array
432 *
433 * @return a pointer to the realloc'd (and possibly moved) array
434 * if succesful
435 *
436 * @return NULL
437 * if unsuccesful
438 */
439 static void *
append_to_pointer_array(void ** array,void * pointer)440 append_to_pointer_array(
441 void **array,
442 void *pointer)
443 {
444 void **newarray = NULL;
445 int i = 0;
446
447 if (array != NULL) {
448 /* Count the elements currently in the array */
449 for (i = 0; array[i] != NULL; ++i);
450 }
451
452 /* realloc, adding a slot for the new pointer */
453 newarray = (void **)realloc(array, (i + 2) * sizeof (*array));
454
455 if (newarray != NULL) {
456 /* Append pointer and terminal NULL */
457 newarray[i] = pointer;
458 newarray[i+1] = NULL;
459 }
460
461 return (newarray);
462 }
463