/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  | }  |