/src/lvm2/libdm/libdm-string.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2006-2015 Red Hat, Inc. All rights reserved. |
3 | | * |
4 | | * This file is part of the device-mapper userspace tools. |
5 | | * |
6 | | * This copyrighted material is made available to anyone wishing to use, |
7 | | * modify, copy, or redistribute it subject to the terms and conditions |
8 | | * of the GNU Lesser General Public License v.2.1. |
9 | | * |
10 | | * You should have received a copy of the GNU Lesser General Public License |
11 | | * along with this program; if not, write to the Free Software Foundation, |
12 | | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
13 | | */ |
14 | | |
15 | | #include "libdm/misc/dmlib.h" |
16 | | |
17 | | #include <ctype.h> |
18 | | #include <stdarg.h> |
19 | | #include <math.h> /* fabs() */ |
20 | | #include <float.h> /* DBL_EPSILON */ |
21 | | |
22 | | /* |
23 | | * consume characters while they match the predicate function. |
24 | | */ |
25 | | static char *_consume(char *buffer, int (*fn) (int)) |
26 | 0 | { |
27 | 0 | while (*buffer && fn(*buffer)) |
28 | 0 | buffer++; |
29 | |
|
30 | 0 | return buffer; |
31 | 0 | } |
32 | | |
33 | | static int _isword(int c) |
34 | 0 | { |
35 | 0 | return !isspace(c); |
36 | 0 | } |
37 | | |
38 | | /* |
39 | | * Split buffer into NULL-separated words in argv. |
40 | | * Returns number of words. |
41 | | */ |
42 | | int dm_split_words(char *buffer, unsigned max, |
43 | | unsigned ignore_comments __attribute__((unused)), |
44 | | char **argv) |
45 | 0 | { |
46 | 0 | unsigned arg; |
47 | |
|
48 | 0 | for (arg = 0; arg < max; arg++) { |
49 | 0 | buffer = _consume(buffer, isspace); |
50 | 0 | if (!*buffer) |
51 | 0 | break; |
52 | | |
53 | 0 | argv[arg] = buffer; |
54 | 0 | buffer = _consume(buffer, _isword); |
55 | |
|
56 | 0 | if (*buffer) { |
57 | 0 | *buffer = '\0'; |
58 | 0 | buffer++; |
59 | 0 | } |
60 | 0 | } |
61 | |
|
62 | 0 | return arg; |
63 | 0 | } |
64 | | |
65 | | /* |
66 | | * Remove hyphen quoting from a component of a name. |
67 | | * NULL-terminates the component and returns start of next component. |
68 | | */ |
69 | | static char *_unquote(char *component) |
70 | 0 | { |
71 | 0 | char *c = component; |
72 | 0 | char *o = c; |
73 | 0 | char *r; |
74 | |
|
75 | 0 | while (*c) { |
76 | 0 | if (*(c + 1)) { |
77 | 0 | if (*c == '-') { |
78 | 0 | if (*(c + 1) == '-') |
79 | 0 | c++; |
80 | 0 | else |
81 | 0 | break; |
82 | 0 | } |
83 | 0 | } |
84 | 0 | *o = *c; |
85 | 0 | o++; |
86 | 0 | c++; |
87 | 0 | } |
88 | |
|
89 | 0 | r = (*c) ? c + 1 : c; |
90 | 0 | *o = '\0'; |
91 | |
|
92 | 0 | return r; |
93 | 0 | } |
94 | | |
95 | | int dm_split_lvm_name(struct dm_pool *mem, const char *dmname, |
96 | | char **vgname, char **lvname, char **layer) |
97 | 0 | { |
98 | 0 | if (!vgname || !lvname || !layer) { |
99 | 0 | log_error(INTERNAL_ERROR "dm_split_lvm_name: Forbidden NULL parameter detected."); |
100 | 0 | return 0; |
101 | 0 | } |
102 | | |
103 | 0 | if (mem && (!dmname || !(*vgname = dm_pool_strdup(mem, dmname)))) { |
104 | 0 | log_error("Failed to duplicate lvm name."); |
105 | 0 | return 0; |
106 | 0 | } else if (!*vgname) { |
107 | 0 | log_error("Missing lvm name for split."); |
108 | 0 | return 0; |
109 | 0 | } |
110 | | |
111 | 0 | _unquote(*layer = _unquote(*lvname = _unquote(*vgname))); |
112 | |
|
113 | 0 | return 1; |
114 | 0 | } |
115 | | |
116 | | /* |
117 | | * On error, up to glibc 2.0.6, snprintf returned -1 if buffer was too small; |
118 | | * From glibc 2.1 it returns number of chars (excl. trailing null) that would |
119 | | * have been written had there been room. |
120 | | * |
121 | | * dm_snprintf reverts to the old behaviour. |
122 | | */ |
123 | | int dm_snprintf(char *buf, size_t bufsize, const char *format, ...) |
124 | 0 | { |
125 | 0 | int n; |
126 | 0 | va_list ap; |
127 | |
|
128 | 0 | va_start(ap, format); |
129 | 0 | n = vsnprintf(buf, bufsize, format, ap); |
130 | 0 | va_end(ap); |
131 | |
|
132 | 0 | if (n < 0 || ((unsigned) n >= bufsize)) |
133 | 0 | return -1; |
134 | | |
135 | 0 | return n; |
136 | 0 | } |
137 | | |
138 | | const char *dm_basename(const char *path) |
139 | 0 | { |
140 | 0 | const char *p = strrchr(path, '/'); |
141 | |
|
142 | 0 | return p ? p + 1 : path; |
143 | 0 | } |
144 | | |
145 | | int dm_vasprintf(char **result, const char *format, va_list aq) |
146 | 0 | { |
147 | 0 | int i, n, size = 16; |
148 | 0 | va_list ap; |
149 | 0 | char *buf = dm_malloc(size); |
150 | |
|
151 | 0 | *result = 0; |
152 | |
|
153 | 0 | if (!buf) |
154 | 0 | return -1; |
155 | | |
156 | 0 | for (i = 0;; i++) { |
157 | 0 | va_copy(ap, aq); |
158 | 0 | n = vsnprintf(buf, size, format, ap); |
159 | 0 | va_end(ap); |
160 | |
|
161 | 0 | if (0 <= n && n < size) |
162 | 0 | break; |
163 | | |
164 | 0 | dm_free(buf); |
165 | | /* Up to glibc 2.0.6 returns -1 */ |
166 | 0 | size = (n < 0) ? size * 2 : n + 1; |
167 | 0 | if (!(buf = dm_malloc(size))) |
168 | 0 | return -1; |
169 | 0 | } |
170 | | |
171 | 0 | if (i > 1) { |
172 | | /* Reallocating more than once? */ |
173 | 0 | if (!(*result = dm_strdup(buf))) { |
174 | 0 | dm_free(buf); |
175 | 0 | return -1; |
176 | 0 | } |
177 | 0 | dm_free(buf); |
178 | 0 | } else |
179 | 0 | *result = buf; |
180 | | |
181 | 0 | return n + 1; |
182 | 0 | } |
183 | | |
184 | | int dm_asprintf(char **result, const char *format, ...) |
185 | 0 | { |
186 | 0 | int r; |
187 | 0 | va_list ap; |
188 | 0 | va_start(ap, format); |
189 | 0 | r = dm_vasprintf(result, format, ap); |
190 | 0 | va_end(ap); |
191 | 0 | return r; |
192 | 0 | } |
193 | | |
194 | | /* |
195 | | * Count occurrences of 'c' in 'str' until we reach a null char. |
196 | | * |
197 | | * Returns: |
198 | | * len - incremented for each char we encounter. |
199 | | * count - number of occurrences of 'c' and 'c2'. |
200 | | */ |
201 | | static void _count_chars(const char *str, size_t *len, int *count, |
202 | | const int c1, const int c2) |
203 | 0 | { |
204 | 0 | const char *ptr; |
205 | |
|
206 | 0 | for (ptr = str; *ptr; ptr++, (*len)++) |
207 | 0 | if (*ptr == c1 || *ptr == c2) |
208 | 0 | (*count)++; |
209 | 0 | } |
210 | | |
211 | | /* |
212 | | * Count occurrences of 'c' in 'str' of length 'size'. |
213 | | * |
214 | | * Returns: |
215 | | * Number of occurrences of 'c' |
216 | | */ |
217 | | unsigned dm_count_chars(const char *str, size_t len, const int c) |
218 | 0 | { |
219 | 0 | size_t i; |
220 | 0 | unsigned count = 0; |
221 | |
|
222 | 0 | for (i = 0; i < len; i++) |
223 | 0 | if (str[i] == c) |
224 | 0 | count++; |
225 | |
|
226 | 0 | return count; |
227 | 0 | } |
228 | | |
229 | | /* |
230 | | * Length of string after escaping double quotes and backslashes. |
231 | | */ |
232 | | size_t dm_escaped_len(const char *str) |
233 | 0 | { |
234 | 0 | size_t len = 1; |
235 | 0 | int count = 0; |
236 | |
|
237 | 0 | _count_chars(str, &len, &count, '\"', '\\'); |
238 | |
|
239 | 0 | return count + len; |
240 | 0 | } |
241 | | |
242 | | /* |
243 | | * Copies a string, quoting orig_char with quote_char. |
244 | | * Optionally also quote quote_char. |
245 | | */ |
246 | | static void _quote_characters(char **out, const char *src, |
247 | | const int orig_char, const int quote_char, |
248 | | int quote_quote_char) |
249 | 0 | { |
250 | 0 | while (*src) { |
251 | 0 | if (*src == orig_char || |
252 | 0 | (*src == quote_char && quote_quote_char)) |
253 | 0 | *(*out)++ = quote_char; |
254 | |
|
255 | 0 | *(*out)++ = *src++; |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | static void _unquote_one_character(char *src, const char orig_char, |
260 | | const char quote_char) |
261 | 0 | { |
262 | 0 | char *out; |
263 | 0 | char s, n; |
264 | | |
265 | | /* Optimise for the common case where no changes are needed. */ |
266 | 0 | while ((s = *src++)) { |
267 | 0 | if (s == quote_char && |
268 | 0 | ((n = *src) == orig_char || n == quote_char)) { |
269 | 0 | out = src++; |
270 | 0 | *(out - 1) = n; |
271 | |
|
272 | 0 | while ((s = *src++)) { |
273 | 0 | if (s == quote_char && |
274 | 0 | ((n = *src) == orig_char || n == quote_char)) { |
275 | 0 | s = n; |
276 | 0 | src++; |
277 | 0 | } |
278 | 0 | *out = s; |
279 | 0 | out++; |
280 | 0 | } |
281 | |
|
282 | 0 | *out = '\0'; |
283 | 0 | return; |
284 | 0 | } |
285 | 0 | } |
286 | 0 | } |
287 | | |
288 | | /* |
289 | | * Unquote each character given in orig_char array and unquote quote_char |
290 | | * as well. Also save the first occurrence of each character from orig_char |
291 | | * that was found unquoted in arr_substr_first_unquoted array. This way we can |
292 | | * process several characters in one go. |
293 | | */ |
294 | | static void _unquote_characters(char *src, const char *orig_chars, |
295 | | size_t num_orig_chars, |
296 | | const char quote_char, |
297 | | char *arr_substr_first_unquoted[]) |
298 | 0 | { |
299 | 0 | char *out = src; |
300 | 0 | char c, s, n; |
301 | 0 | unsigned i; |
302 | |
|
303 | 0 | while ((s = *src++)) { |
304 | 0 | for (i = 0; i < num_orig_chars; i++) { |
305 | 0 | c = orig_chars[i]; |
306 | 0 | if (s == quote_char && |
307 | 0 | ((n = *src) == c || n == quote_char)) { |
308 | 0 | s = n; |
309 | 0 | src++; |
310 | 0 | break; |
311 | 0 | } |
312 | 0 | if (arr_substr_first_unquoted && (s == c) && |
313 | 0 | !arr_substr_first_unquoted[i]) |
314 | 0 | arr_substr_first_unquoted[i] = out; |
315 | 0 | }; |
316 | 0 | *out++ = s; |
317 | 0 | } |
318 | |
|
319 | 0 | *out = '\0'; |
320 | 0 | } |
321 | | |
322 | | /* |
323 | | * Copies a string, quoting hyphens with hyphens. |
324 | | */ |
325 | | static void _quote_hyphens(char **out, const char *src) |
326 | 0 | { |
327 | 0 | _quote_characters(out, src, '-', '-', 0); |
328 | 0 | } |
329 | | |
330 | | /* |
331 | | * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>. |
332 | | */ |
333 | | char *dm_build_dm_name(struct dm_pool *mem, const char *vgname, |
334 | | const char *lvname, const char *layer) |
335 | 0 | { |
336 | 0 | size_t len = 1; |
337 | 0 | int hyphens = 1; |
338 | 0 | char *r, *out; |
339 | |
|
340 | 0 | _count_chars(vgname, &len, &hyphens, '-', 0); |
341 | 0 | _count_chars(lvname, &len, &hyphens, '-', 0); |
342 | |
|
343 | 0 | if (layer && *layer) { |
344 | 0 | _count_chars(layer, &len, &hyphens, '-', 0); |
345 | 0 | hyphens++; |
346 | 0 | } |
347 | |
|
348 | 0 | len += hyphens; |
349 | |
|
350 | 0 | if (!(r = dm_pool_alloc(mem, len))) { |
351 | 0 | log_error("build_dm_name: Allocation failed for %" PRIsize_t |
352 | 0 | " for %s %s %s.", len, vgname, lvname, layer); |
353 | 0 | return NULL; |
354 | 0 | } |
355 | | |
356 | 0 | out = r; |
357 | 0 | _quote_hyphens(&out, vgname); |
358 | 0 | *out++ = '-'; |
359 | 0 | _quote_hyphens(&out, lvname); |
360 | |
|
361 | 0 | if (layer && *layer) { |
362 | | /* No hyphen if the layer begins with _ e.g. _mlog */ |
363 | 0 | if (*layer != '_') |
364 | 0 | *out++ = '-'; |
365 | 0 | _quote_hyphens(&out, layer); |
366 | 0 | } |
367 | 0 | *out = '\0'; |
368 | |
|
369 | 0 | return r; |
370 | 0 | } |
371 | | |
372 | | char *dm_build_dm_uuid(struct dm_pool *mem, const char *uuid_prefix, const char *lvid, const char *layer) |
373 | 0 | { |
374 | 0 | char *dmuuid; |
375 | 0 | size_t len; |
376 | |
|
377 | 0 | if (!layer) |
378 | 0 | layer = ""; |
379 | |
|
380 | 0 | len = strlen(uuid_prefix) + strlen(lvid) + strlen(layer) + 2; |
381 | |
|
382 | 0 | if (!(dmuuid = dm_pool_alloc(mem, len))) { |
383 | 0 | log_error("build_dm_name: Allocation failed for %" PRIsize_t |
384 | 0 | " %s %s.", len, lvid, layer); |
385 | 0 | return NULL; |
386 | 0 | } |
387 | | |
388 | 0 | snprintf(dmuuid, len, "%s%s%s%s", uuid_prefix, lvid, (*layer) ? "-" : "", layer); |
389 | |
|
390 | 0 | return dmuuid; |
391 | 0 | } |
392 | | |
393 | | /* |
394 | | * Copies a string, quoting double quotes with backslashes. |
395 | | */ |
396 | | char *dm_escape_double_quotes(char *out, const char *src) |
397 | 0 | { |
398 | 0 | char *buf = out; |
399 | |
|
400 | 0 | _quote_characters(&buf, src, '\"', '\\', 1); |
401 | 0 | *buf = '\0'; |
402 | |
|
403 | 0 | return out; |
404 | 0 | } |
405 | | |
406 | | /* |
407 | | * Undo quoting in situ. |
408 | | */ |
409 | | void dm_unescape_double_quotes(char *src) |
410 | 0 | { |
411 | 0 | _unquote_one_character(src, '\"', '\\'); |
412 | 0 | } |
413 | | |
414 | | /* |
415 | | * Unescape colons and "at" signs in situ and save the substrings |
416 | | * starting at the position of the first unescaped colon and the |
417 | | * first unescaped "at" sign. This is normally used to unescape |
418 | | * device names used as PVs. |
419 | | */ |
420 | | void dm_unescape_colons_and_at_signs(char *src, |
421 | | char **substr_first_unquoted_colon, |
422 | | char **substr_first_unquoted_at_sign) |
423 | 0 | { |
424 | 0 | const char *orig_chars = ":@"; |
425 | 0 | char *arr_substr_first_unquoted[] = {NULL, NULL, NULL}; |
426 | |
|
427 | 0 | _unquote_characters(src, orig_chars, 2, '\\', arr_substr_first_unquoted); |
428 | |
|
429 | 0 | if (substr_first_unquoted_colon) |
430 | 0 | *substr_first_unquoted_colon = arr_substr_first_unquoted[0]; |
431 | |
|
432 | 0 | if (substr_first_unquoted_at_sign) |
433 | 0 | *substr_first_unquoted_at_sign = arr_substr_first_unquoted[1]; |
434 | 0 | } |
435 | | |
436 | | int dm_strncpy(char *dest, const char *src, size_t n) |
437 | 0 | { |
438 | 0 | if (memccpy(dest, src, 0, n)) |
439 | 0 | return 1; |
440 | | |
441 | 0 | if (n > 0) |
442 | 0 | dest[n - 1] = '\0'; |
443 | |
|
444 | 0 | return 0; |
445 | 0 | } |
446 | | |
447 | | /* Test if the doubles are close enough to be considered equal */ |
448 | | static int _close_enough(double d1, double d2) |
449 | 0 | { |
450 | 0 | return fabs(d1 - d2) < DBL_EPSILON; |
451 | 0 | } |
452 | | |
453 | 0 | #define BASE_UNKNOWN 0 |
454 | 0 | #define BASE_SHARED 1 |
455 | 0 | #define BASE_1024 8 |
456 | 0 | #define BASE_1000 15 |
457 | 0 | #define BASE_SPECIAL 21 |
458 | 0 | #define NUM_UNIT_PREFIXES 6 |
459 | 0 | #define NUM_SPECIAL 3 |
460 | | |
461 | 0 | #define SIZE_BUF 128 |
462 | | |
463 | | const char *dm_size_to_string(struct dm_pool *mem, uint64_t size, |
464 | | char unit_type, int use_si_units, |
465 | | uint64_t unit_factor, int include_suffix, |
466 | | dm_size_suffix_t suffix_type) |
467 | 0 | { |
468 | 0 | unsigned base = BASE_UNKNOWN; |
469 | 0 | unsigned s; |
470 | 0 | int precision; |
471 | 0 | double d; |
472 | 0 | uint64_t byte = UINT64_C(0); |
473 | 0 | uint64_t units = UINT64_C(1024); |
474 | 0 | char *size_buf; |
475 | 0 | char new_unit_type = '\0', unit_type_buf[2]; |
476 | 0 | const char *prefix = ""; |
477 | 0 | static const char _size_str[][3][12] = { |
478 | | /* BASE_UNKNOWN */ |
479 | 0 | {" ", " ", " "}, /* [0] */ |
480 | | |
481 | | /* BASE_SHARED - Used if use_si_units = 0 */ |
482 | 0 | {" Exabyte", " EB", "E"}, /* [1] */ |
483 | 0 | {" Petabyte", " PB", "P"}, /* [2] */ |
484 | 0 | {" Terabyte", " TB", "T"}, /* [3] */ |
485 | 0 | {" Gigabyte", " GB", "G"}, /* [4] */ |
486 | 0 | {" Megabyte", " MB", "M"}, /* [5] */ |
487 | 0 | {" Kilobyte", " KB", "K"}, /* [6] */ |
488 | 0 | {" Byte ", " B", "B"}, /* [7] */ |
489 | | |
490 | | /* BASE_1024 - Used if use_si_units = 1 */ |
491 | 0 | {" Exbibyte", " EiB", "e"}, /* [8] */ |
492 | 0 | {" Pebibyte", " PiB", "p"}, /* [9] */ |
493 | 0 | {" Tebibyte", " TiB", "t"}, /* [10] */ |
494 | 0 | {" Gibibyte", " GiB", "g"}, /* [11] */ |
495 | 0 | {" Mebibyte", " MiB", "m"}, /* [12] */ |
496 | 0 | {" Kibibyte", " KiB", "k"}, /* [13] */ |
497 | 0 | {" Byte ", " B", "b"}, /* [14] */ |
498 | | |
499 | | /* BASE_1000 - Used if use_si_units = 1 */ |
500 | 0 | {" Exabyte", " EB", "E"}, /* [15] */ |
501 | 0 | {" Petabyte", " PB", "P"}, /* [16] */ |
502 | 0 | {" Terabyte", " TB", "T"}, /* [17] */ |
503 | 0 | {" Gigabyte", " GB", "G"}, /* [18] */ |
504 | 0 | {" Megabyte", " MB", "M"}, /* [19] */ |
505 | 0 | {" Kilobyte", " kB", "K"}, /* [20] */ |
506 | | |
507 | | /* BASE_SPECIAL */ |
508 | 0 | {" Byte ", " B ", "B"}, /* [21] (shared with BASE_1000) */ |
509 | 0 | {" Units ", " Un", "U"}, /* [22] */ |
510 | 0 | {" Sectors ", " Se", "S"}, /* [23] */ |
511 | 0 | }; |
512 | |
|
513 | 0 | if (!(size_buf = dm_pool_alloc(mem, SIZE_BUF))) { |
514 | 0 | log_error("no memory for size display buffer"); |
515 | 0 | return ""; |
516 | 0 | } |
517 | | |
518 | 0 | if (!use_si_units) { |
519 | | /* Case-independent match */ |
520 | 0 | for (s = 0; s < NUM_UNIT_PREFIXES; s++) |
521 | 0 | if (toupper((int) unit_type) == |
522 | 0 | *_size_str[BASE_SHARED + s][2]) { |
523 | 0 | base = BASE_SHARED; |
524 | 0 | break; |
525 | 0 | } |
526 | 0 | } else { |
527 | | /* Case-dependent match for powers of 1000 */ |
528 | 0 | for (s = 0; s < NUM_UNIT_PREFIXES; s++) |
529 | 0 | if (unit_type == *_size_str[BASE_1000 + s][2]) { |
530 | 0 | base = BASE_1000; |
531 | 0 | break; |
532 | 0 | } |
533 | | |
534 | | /* Case-dependent match for powers of 1024 */ |
535 | 0 | if (base == BASE_UNKNOWN) |
536 | 0 | for (s = 0; s < NUM_UNIT_PREFIXES; s++) |
537 | 0 | if (unit_type == *_size_str[BASE_1024 + s][2]) { |
538 | 0 | base = BASE_1024; |
539 | 0 | break; |
540 | 0 | } |
541 | 0 | } |
542 | |
|
543 | 0 | if (base == BASE_UNKNOWN) |
544 | | /* Check for special units - s, b or u */ |
545 | 0 | for (s = 0; s < NUM_SPECIAL; s++) |
546 | 0 | if (toupper((int) unit_type) == |
547 | 0 | *_size_str[BASE_SPECIAL + s][2]) { |
548 | 0 | base = BASE_SPECIAL; |
549 | 0 | break; |
550 | 0 | } |
551 | |
|
552 | 0 | if (size == UINT64_C(0)) { |
553 | 0 | if (base == BASE_UNKNOWN) |
554 | 0 | s = 0; |
555 | 0 | snprintf(size_buf, SIZE_BUF, "0%s", include_suffix ? _size_str[base + s][suffix_type] : ""); |
556 | 0 | return size_buf; |
557 | 0 | } |
558 | | |
559 | 0 | size *= UINT64_C(512); |
560 | |
|
561 | 0 | if (base != BASE_UNKNOWN) { |
562 | 0 | if (!unit_factor) { |
563 | 0 | unit_type_buf[0] = unit_type; |
564 | 0 | unit_type_buf[1] = '\0'; |
565 | 0 | if (!(unit_factor = dm_units_to_factor(&unit_type_buf[0], &new_unit_type, 1, NULL)) || |
566 | 0 | unit_type != new_unit_type) { |
567 | | /* The two functions should match (and unrecognised units get treated like 'h'). */ |
568 | 0 | log_error(INTERNAL_ERROR "Inconsistent units: %c and %c.", unit_type, new_unit_type); |
569 | 0 | return ""; |
570 | 0 | } |
571 | 0 | } |
572 | 0 | byte = unit_factor; |
573 | 0 | } else { |
574 | | /* Human-readable style */ |
575 | 0 | if (unit_type == 'H' || unit_type == 'R') { |
576 | 0 | units = UINT64_C(1000); |
577 | 0 | base = BASE_1000; |
578 | 0 | } else { |
579 | 0 | units = UINT64_C(1024); |
580 | 0 | base = BASE_1024; |
581 | 0 | } |
582 | |
|
583 | 0 | if (!use_si_units) |
584 | 0 | base = BASE_SHARED; |
585 | |
|
586 | 0 | byte = units * units * units * units * units * units; |
587 | |
|
588 | 0 | for (s = 0; s < NUM_UNIT_PREFIXES && size < byte; s++) |
589 | 0 | byte /= units; |
590 | |
|
591 | 0 | if ((s < NUM_UNIT_PREFIXES) && |
592 | 0 | ((unit_type == 'R') || (unit_type == 'r'))) { |
593 | | /* When the rounding would cause difference, add '<' prefix |
594 | | * i.e. 2043M is more than 1.9949G prints <2.00G |
595 | | * This version is for 2 digits fixed precision */ |
596 | 0 | d = 100. * (double) size / byte; |
597 | 0 | if (!_close_enough(floorl(d), nearbyintl(d))) |
598 | 0 | prefix = "<"; |
599 | 0 | } |
600 | |
|
601 | 0 | include_suffix = 1; |
602 | 0 | } |
603 | | |
604 | | /* FIXME Make precision configurable */ |
605 | 0 | switch (toupper(*_size_str[base + s][DM_SIZE_UNIT])) { |
606 | 0 | case 'B': |
607 | 0 | case 'S': |
608 | 0 | precision = 0; |
609 | 0 | break; |
610 | 0 | default: |
611 | 0 | precision = 2; |
612 | 0 | } |
613 | | |
614 | 0 | snprintf(size_buf, SIZE_BUF, "%s%.*f%s", prefix, precision, |
615 | 0 | (double) size / byte, include_suffix ? _size_str[base + s][suffix_type] : ""); |
616 | |
|
617 | 0 | return size_buf; |
618 | 0 | } |
619 | | |
620 | | uint64_t dm_units_to_factor(const char *units, char *unit_type, |
621 | | int strict, const char **endptr) |
622 | 0 | { |
623 | 0 | char *ptr = NULL; |
624 | 0 | uint64_t v; |
625 | 0 | double custom_value = 0; |
626 | 0 | uint64_t multiplier; |
627 | |
|
628 | 0 | if (endptr) |
629 | 0 | *endptr = units; |
630 | |
|
631 | 0 | if (isdigit(*units)) { |
632 | 0 | custom_value = strtod(units, &ptr); |
633 | 0 | if (ptr == units) |
634 | 0 | return 0; |
635 | 0 | v = (uint64_t) strtoull(units, NULL, 10); |
636 | 0 | if (_close_enough((double) v, custom_value)) |
637 | 0 | custom_value = 0; /* Use integer arithmetic */ |
638 | 0 | units = ptr; |
639 | 0 | } else |
640 | 0 | v = 1; |
641 | | |
642 | | /* Only one units char permitted in strict mode. */ |
643 | 0 | if (strict && units[0] && units[1]) |
644 | 0 | return 0; |
645 | | |
646 | 0 | if (v == 1) |
647 | 0 | *unit_type = *units; |
648 | 0 | else |
649 | 0 | *unit_type = 'U'; |
650 | |
|
651 | 0 | switch (*units) { |
652 | 0 | case 'h': |
653 | 0 | case 'H': |
654 | 0 | case 'r': |
655 | 0 | case 'R': |
656 | 0 | multiplier = v = UINT64_C(1); |
657 | 0 | *unit_type = *units; |
658 | 0 | break; |
659 | 0 | case 'b': |
660 | 0 | case 'B': |
661 | 0 | multiplier = UINT64_C(1); |
662 | 0 | break; |
663 | 0 | #define KILO UINT64_C(1024) |
664 | 0 | case 's': |
665 | 0 | case 'S': |
666 | 0 | multiplier = (KILO/2); |
667 | 0 | break; |
668 | 0 | case 'k': |
669 | 0 | multiplier = KILO; |
670 | 0 | break; |
671 | 0 | case 'm': |
672 | 0 | multiplier = KILO * KILO; |
673 | 0 | break; |
674 | 0 | case 'g': |
675 | 0 | multiplier = KILO * KILO * KILO; |
676 | 0 | break; |
677 | 0 | case 't': |
678 | 0 | multiplier = KILO * KILO * KILO * KILO; |
679 | 0 | break; |
680 | 0 | case 'p': |
681 | 0 | multiplier = KILO * KILO * KILO * KILO * KILO; |
682 | 0 | break; |
683 | 0 | case 'e': |
684 | 0 | multiplier = KILO * KILO * KILO * KILO * KILO * KILO; |
685 | 0 | break; |
686 | 0 | #undef KILO |
687 | 0 | #define KILO UINT64_C(1000) |
688 | 0 | case 'K': |
689 | 0 | multiplier = KILO; |
690 | 0 | break; |
691 | 0 | case 'M': |
692 | 0 | multiplier = KILO * KILO; |
693 | 0 | break; |
694 | 0 | case 'G': |
695 | 0 | multiplier = KILO * KILO * KILO; |
696 | 0 | break; |
697 | 0 | case 'T': |
698 | 0 | multiplier = KILO * KILO * KILO * KILO; |
699 | 0 | break; |
700 | 0 | case 'P': |
701 | 0 | multiplier = KILO * KILO * KILO * KILO * KILO; |
702 | 0 | break; |
703 | 0 | case 'E': |
704 | 0 | multiplier = KILO * KILO * KILO * KILO * KILO * KILO; |
705 | 0 | break; |
706 | 0 | #undef KILO |
707 | 0 | default: |
708 | 0 | return 0; |
709 | 0 | } |
710 | | |
711 | 0 | if (endptr) |
712 | 0 | *endptr = units + 1; |
713 | |
|
714 | 0 | if (_close_enough(custom_value, 0.)) |
715 | 0 | return v * multiplier; /* Use integer arithmetic */ |
716 | 0 | else |
717 | 0 | return (uint64_t) (custom_value * multiplier); |
718 | 0 | } |