1 /* 2 * Helpers for formatting and printing strings 3 * 4 * Copyright 31 August 2008 James Bottomley 5 * Copyright (C) 2013, Intel Corporation 6 */ 7 #include <linux/kernel.h> 8 #include <linux/math64.h> 9 #include <linux/export.h> 10 #include <linux/ctype.h> 11 #include <linux/string_helpers.h> 12 13 /** 14 * string_get_size - get the size in the specified units 15 * @size: The size to be converted 16 * @units: units to use (powers of 1000 or 1024) 17 * @buf: buffer to format to 18 * @len: length of buffer 19 * 20 * This function returns a string formatted to 3 significant figures 21 * giving the size in the required units. Returns 0 on success or 22 * error on failure. @buf is always zero terminated. 23 * 24 */ 25 int string_get_size(u64 size, const enum string_size_units units, 26 char *buf, int len) 27 { 28 static const char *const units_10[] = { 29 "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL 30 }; 31 static const char *const units_2[] = { 32 "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", 33 NULL 34 }; 35 static const char *const *const units_str[] = { 36 [STRING_UNITS_10] = units_10, 37 [STRING_UNITS_2] = units_2, 38 }; 39 static const unsigned int divisor[] = { 40 [STRING_UNITS_10] = 1000, 41 [STRING_UNITS_2] = 1024, 42 }; 43 int i, j; 44 u64 remainder = 0, sf_cap; 45 char tmp[8]; 46 47 tmp[0] = '\0'; 48 i = 0; 49 if (size >= divisor[units]) { 50 while (size >= divisor[units] && units_str[units][i]) { 51 remainder = do_div(size, divisor[units]); 52 i++; 53 } 54 55 sf_cap = size; 56 for (j = 0; sf_cap*10 < 1000; j++) 57 sf_cap *= 10; 58 59 if (j) { 60 remainder *= 1000; 61 do_div(remainder, divisor[units]); 62 snprintf(tmp, sizeof(tmp), ".%03lld", 63 (unsigned long long)remainder); 64 tmp[j+1] = '\0'; 65 } 66 } 67 68 snprintf(buf, len, "%lld%s %s", (unsigned long long)size, 69 tmp, units_str[units][i]); 70 71 return 0; 72 } 73 EXPORT_SYMBOL(string_get_size); 74 75 static bool unescape_space(char **src, char **dst) 76 { 77 char *p = *dst, *q = *src; 78 79 switch (*q) { 80 case 'n': 81 *p = '\n'; 82 break; 83 case 'r': 84 *p = '\r'; 85 break; 86 case 't': 87 *p = '\t'; 88 break; 89 case 'v': 90 *p = '\v'; 91 break; 92 case 'f': 93 *p = '\f'; 94 break; 95 default: 96 return false; 97 } 98 *dst += 1; 99 *src += 1; 100 return true; 101 } 102 103 static bool unescape_octal(char **src, char **dst) 104 { 105 char *p = *dst, *q = *src; 106 u8 num; 107 108 if (isodigit(*q) == 0) 109 return false; 110 111 num = (*q++) & 7; 112 while (num < 32 && isodigit(*q) && (q - *src < 3)) { 113 num <<= 3; 114 num += (*q++) & 7; 115 } 116 *p = num; 117 *dst += 1; 118 *src = q; 119 return true; 120 } 121 122 static bool unescape_hex(char **src, char **dst) 123 { 124 char *p = *dst, *q = *src; 125 int digit; 126 u8 num; 127 128 if (*q++ != 'x') 129 return false; 130 131 num = digit = hex_to_bin(*q++); 132 if (digit < 0) 133 return false; 134 135 digit = hex_to_bin(*q); 136 if (digit >= 0) { 137 q++; 138 num = (num << 4) | digit; 139 } 140 *p = num; 141 *dst += 1; 142 *src = q; 143 return true; 144 } 145 146 static bool unescape_special(char **src, char **dst) 147 { 148 char *p = *dst, *q = *src; 149 150 switch (*q) { 151 case '\"': 152 *p = '\"'; 153 break; 154 case '\\': 155 *p = '\\'; 156 break; 157 case 'a': 158 *p = '\a'; 159 break; 160 case 'e': 161 *p = '\e'; 162 break; 163 default: 164 return false; 165 } 166 *dst += 1; 167 *src += 1; 168 return true; 169 } 170 171 int string_unescape(char *src, char *dst, size_t size, unsigned int flags) 172 { 173 char *out = dst; 174 175 while (*src && --size) { 176 if (src[0] == '\\' && src[1] != '\0' && size > 1) { 177 src++; 178 size--; 179 180 if (flags & UNESCAPE_SPACE && 181 unescape_space(&src, &out)) 182 continue; 183 184 if (flags & UNESCAPE_OCTAL && 185 unescape_octal(&src, &out)) 186 continue; 187 188 if (flags & UNESCAPE_HEX && 189 unescape_hex(&src, &out)) 190 continue; 191 192 if (flags & UNESCAPE_SPECIAL && 193 unescape_special(&src, &out)) 194 continue; 195 196 *out++ = '\\'; 197 } 198 *out++ = *src++; 199 } 200 *out = '\0'; 201 202 return out - dst; 203 } 204 EXPORT_SYMBOL(string_unescape); 205