1 /* 2 * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <string.h> 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <inttypes.h> 34 #include <ctype.h> 35 #include <errno.h> 36 37 #include "humanize.h" 38 39 static const char prefixes[] = " KMGTPE"; 40 int 41 _humanize_number(char *buf, size_t bufsz, uint64_t num) 42 { 43 const char *prefixp; 44 int ret; 45 uint64_t i, d; 46 47 prefixp = prefixes; 48 i = num; 49 d = 0; 50 51 while ((i > 1024) && (*prefixp != '\0')) { 52 d = (i % 1024)/10; 53 i /= 1024; 54 ++prefixp; 55 } 56 57 if (d > 0) 58 ret = snprintf(buf, bufsz, "%"PRIu64".%"PRIu64" %c", 59 i, d, *prefixp); 60 else 61 ret = snprintf(buf, bufsz, "%"PRIu64" %c", i, *prefixp); 62 63 64 if ((ret < 0) || ((size_t)ret >= bufsz)) { 65 errno = ENOMEM; 66 return -1; 67 } else { 68 return 0; 69 } 70 } 71 72 int 73 _dehumanize_number(const char *buf, uint64_t *dest) 74 { 75 char *endptr; 76 uint64_t n, n_check, d; 77 uint64_t multiplier; 78 size_t len; 79 80 if (*buf == '\0') { 81 errno = EINVAL; 82 return -1; 83 } 84 85 len = strlen(buf); 86 if (tolower(buf[len-1]) == 'b') 87 --len; 88 89 multiplier = 1; 90 91 switch (tolower(buf[len-1])) { 92 case 'y': 93 multiplier *= 1024; 94 case 'z': 95 multiplier *= 1024; 96 case 'e': 97 multiplier *= 1024; 98 case 'p': 99 multiplier *= 1024; 100 case 't': 101 multiplier *= 1024; 102 case 'g': 103 multiplier *= 1024; 104 case 'm': 105 multiplier *= 1024; 106 case 'k': 107 multiplier *= 1024; 108 break; 109 default: 110 /* 111 * only set error if string ends in a character that 112 * is not a valid unit. 113 */ 114 if (isalpha(buf[len-1])) { 115 errno = EINVAL; 116 return -1; 117 } 118 } 119 120 d = 0; 121 n = n_check = strtoull(buf, &endptr, 10); 122 if (endptr) { 123 if ((*endptr != '.') && (*endptr != '\0') && 124 (*endptr != ' ') && (endptr != &buf[len-1])) { 125 errno = EINVAL; 126 return -1; 127 } 128 129 if (*endptr == '.') { 130 d = strtoull(endptr+1, &endptr, 10); 131 if (endptr && (*endptr != '\0') && 132 (*endptr != ' ') && 133 (endptr != &buf[len-1])) { 134 errno = EINVAL; 135 return -1; 136 } 137 } 138 } 139 140 if (d != 0) { 141 while (d < 100) 142 d *= 10; 143 144 while (d > 1000) 145 d /= 10; 146 } 147 148 d *= (multiplier/1024); 149 n *= multiplier; 150 151 if ((uint64_t)(n/multiplier) != n_check) { 152 errno = ERANGE; 153 return -1; 154 } 155 156 n += d; 157 *dest = n; 158 159 return 0; 160 } 161 162 #ifdef __TEST_HUMANIZE__ 163 164 #include <assert.h> 165 int main(int argc, char *argv[]) 166 { 167 char buf[1024]; 168 uint64_t n, out; 169 int err; 170 171 if (argc < 3) 172 return -1; 173 174 n = strtoull(argv[1], NULL, 10); 175 176 err = _humanize_number(buf, 1024, n); 177 assert(err == 0); 178 179 err = _dehumanize_number(buf, &out); 180 assert(err == 0); 181 182 printf("Converting: %"PRIu64" => %s => %"PRIu64"\n", n, buf, out); 183 184 err = _dehumanize_number(argv[2], &out); 185 assert (err == 0); 186 187 printf("Converting: %s => %"PRIu64"\n", argv[2], out); 188 189 return 0; 190 } 191 192 #endif 193