xref: /openbmc/linux/lib/string_helpers.c (revision 5a86bf34)
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 *units_10[] = { "B", "kB", "MB", "GB", "TB", "PB",
29 				   "EB", "ZB", "YB", NULL};
30 	static const char *units_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB",
31 				 "EiB", "ZiB", "YiB", NULL };
32 	static const char **units_str[] = {
33 		[STRING_UNITS_10] =  units_10,
34 		[STRING_UNITS_2] = units_2,
35 	};
36 	static const unsigned int divisor[] = {
37 		[STRING_UNITS_10] = 1000,
38 		[STRING_UNITS_2] = 1024,
39 	};
40 	int i, j;
41 	u64 remainder = 0, sf_cap;
42 	char tmp[8];
43 
44 	tmp[0] = '\0';
45 	i = 0;
46 	if (size >= divisor[units]) {
47 		while (size >= divisor[units] && units_str[units][i]) {
48 			remainder = do_div(size, divisor[units]);
49 			i++;
50 		}
51 
52 		sf_cap = size;
53 		for (j = 0; sf_cap*10 < 1000; j++)
54 			sf_cap *= 10;
55 
56 		if (j) {
57 			remainder *= 1000;
58 			do_div(remainder, divisor[units]);
59 			snprintf(tmp, sizeof(tmp), ".%03lld",
60 				 (unsigned long long)remainder);
61 			tmp[j+1] = '\0';
62 		}
63 	}
64 
65 	snprintf(buf, len, "%lld%s %s", (unsigned long long)size,
66 		 tmp, units_str[units][i]);
67 
68 	return 0;
69 }
70 EXPORT_SYMBOL(string_get_size);
71 
72 static bool unescape_space(char **src, char **dst)
73 {
74 	char *p = *dst, *q = *src;
75 
76 	switch (*q) {
77 	case 'n':
78 		*p = '\n';
79 		break;
80 	case 'r':
81 		*p = '\r';
82 		break;
83 	case 't':
84 		*p = '\t';
85 		break;
86 	case 'v':
87 		*p = '\v';
88 		break;
89 	case 'f':
90 		*p = '\f';
91 		break;
92 	default:
93 		return false;
94 	}
95 	*dst += 1;
96 	*src += 1;
97 	return true;
98 }
99 
100 static bool unescape_octal(char **src, char **dst)
101 {
102 	char *p = *dst, *q = *src;
103 	u8 num;
104 
105 	if (isodigit(*q) == 0)
106 		return false;
107 
108 	num = (*q++) & 7;
109 	while (num < 32 && isodigit(*q) && (q - *src < 3)) {
110 		num <<= 3;
111 		num += (*q++) & 7;
112 	}
113 	*p = num;
114 	*dst += 1;
115 	*src = q;
116 	return true;
117 }
118 
119 static bool unescape_hex(char **src, char **dst)
120 {
121 	char *p = *dst, *q = *src;
122 	int digit;
123 	u8 num;
124 
125 	if (*q++ != 'x')
126 		return false;
127 
128 	num = digit = hex_to_bin(*q++);
129 	if (digit < 0)
130 		return false;
131 
132 	digit = hex_to_bin(*q);
133 	if (digit >= 0) {
134 		q++;
135 		num = (num << 4) | digit;
136 	}
137 	*p = num;
138 	*dst += 1;
139 	*src = q;
140 	return true;
141 }
142 
143 static bool unescape_special(char **src, char **dst)
144 {
145 	char *p = *dst, *q = *src;
146 
147 	switch (*q) {
148 	case '\"':
149 		*p = '\"';
150 		break;
151 	case '\\':
152 		*p = '\\';
153 		break;
154 	case 'a':
155 		*p = '\a';
156 		break;
157 	case 'e':
158 		*p = '\e';
159 		break;
160 	default:
161 		return false;
162 	}
163 	*dst += 1;
164 	*src += 1;
165 	return true;
166 }
167 
168 int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
169 {
170 	char *out = dst;
171 
172 	while (*src && --size) {
173 		if (src[0] == '\\' && src[1] != '\0' && size > 1) {
174 			src++;
175 			size--;
176 
177 			if (flags & UNESCAPE_SPACE &&
178 					unescape_space(&src, &out))
179 				continue;
180 
181 			if (flags & UNESCAPE_OCTAL &&
182 					unescape_octal(&src, &out))
183 				continue;
184 
185 			if (flags & UNESCAPE_HEX &&
186 					unescape_hex(&src, &out))
187 				continue;
188 
189 			if (flags & UNESCAPE_SPECIAL &&
190 					unescape_special(&src, &out))
191 				continue;
192 
193 			*out++ = '\\';
194 		}
195 		*out++ = *src++;
196 	}
197 	*out = '\0';
198 
199 	return out - dst;
200 }
201 EXPORT_SYMBOL(string_unescape);
202