xref: /linux/lib/string_helpers.c (revision 29075feaf1f55e6b1aa4054b44bc141e8d5eab0b)
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