xref: /openbmc/linux/lib/string_helpers.c (revision e1f7c9ee)
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/errno.h>
12 #include <linux/string.h>
13 #include <linux/string_helpers.h>
14 
15 /**
16  * string_get_size - get the size in the specified units
17  * @size:	The size to be converted
18  * @units:	units to use (powers of 1000 or 1024)
19  * @buf:	buffer to format to
20  * @len:	length of buffer
21  *
22  * This function returns a string formatted to 3 significant figures
23  * giving the size in the required units.  Returns 0 on success or
24  * error on failure.  @buf is always zero terminated.
25  *
26  */
27 int string_get_size(u64 size, const enum string_size_units units,
28 		    char *buf, int len)
29 {
30 	static const char *const units_10[] = {
31 		"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL
32 	};
33 	static const char *const units_2[] = {
34 		"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB",
35 		NULL
36 	};
37 	static const char *const *const units_str[] = {
38 		[STRING_UNITS_10] = units_10,
39 		[STRING_UNITS_2] = units_2,
40 	};
41 	static const unsigned int divisor[] = {
42 		[STRING_UNITS_10] = 1000,
43 		[STRING_UNITS_2] = 1024,
44 	};
45 	int i, j;
46 	u64 remainder = 0, sf_cap;
47 	char tmp[8];
48 
49 	tmp[0] = '\0';
50 	i = 0;
51 	if (size >= divisor[units]) {
52 		while (size >= divisor[units] && units_str[units][i]) {
53 			remainder = do_div(size, divisor[units]);
54 			i++;
55 		}
56 
57 		sf_cap = size;
58 		for (j = 0; sf_cap*10 < 1000; j++)
59 			sf_cap *= 10;
60 
61 		if (j) {
62 			remainder *= 1000;
63 			do_div(remainder, divisor[units]);
64 			snprintf(tmp, sizeof(tmp), ".%03lld",
65 				 (unsigned long long)remainder);
66 			tmp[j+1] = '\0';
67 		}
68 	}
69 
70 	snprintf(buf, len, "%lld%s %s", (unsigned long long)size,
71 		 tmp, units_str[units][i]);
72 
73 	return 0;
74 }
75 EXPORT_SYMBOL(string_get_size);
76 
77 static bool unescape_space(char **src, char **dst)
78 {
79 	char *p = *dst, *q = *src;
80 
81 	switch (*q) {
82 	case 'n':
83 		*p = '\n';
84 		break;
85 	case 'r':
86 		*p = '\r';
87 		break;
88 	case 't':
89 		*p = '\t';
90 		break;
91 	case 'v':
92 		*p = '\v';
93 		break;
94 	case 'f':
95 		*p = '\f';
96 		break;
97 	default:
98 		return false;
99 	}
100 	*dst += 1;
101 	*src += 1;
102 	return true;
103 }
104 
105 static bool unescape_octal(char **src, char **dst)
106 {
107 	char *p = *dst, *q = *src;
108 	u8 num;
109 
110 	if (isodigit(*q) == 0)
111 		return false;
112 
113 	num = (*q++) & 7;
114 	while (num < 32 && isodigit(*q) && (q - *src < 3)) {
115 		num <<= 3;
116 		num += (*q++) & 7;
117 	}
118 	*p = num;
119 	*dst += 1;
120 	*src = q;
121 	return true;
122 }
123 
124 static bool unescape_hex(char **src, char **dst)
125 {
126 	char *p = *dst, *q = *src;
127 	int digit;
128 	u8 num;
129 
130 	if (*q++ != 'x')
131 		return false;
132 
133 	num = digit = hex_to_bin(*q++);
134 	if (digit < 0)
135 		return false;
136 
137 	digit = hex_to_bin(*q);
138 	if (digit >= 0) {
139 		q++;
140 		num = (num << 4) | digit;
141 	}
142 	*p = num;
143 	*dst += 1;
144 	*src = q;
145 	return true;
146 }
147 
148 static bool unescape_special(char **src, char **dst)
149 {
150 	char *p = *dst, *q = *src;
151 
152 	switch (*q) {
153 	case '\"':
154 		*p = '\"';
155 		break;
156 	case '\\':
157 		*p = '\\';
158 		break;
159 	case 'a':
160 		*p = '\a';
161 		break;
162 	case 'e':
163 		*p = '\e';
164 		break;
165 	default:
166 		return false;
167 	}
168 	*dst += 1;
169 	*src += 1;
170 	return true;
171 }
172 
173 /**
174  * string_unescape - unquote characters in the given string
175  * @src:	source buffer (escaped)
176  * @dst:	destination buffer (unescaped)
177  * @size:	size of the destination buffer (0 to unlimit)
178  * @flags:	combination of the flags (bitwise OR):
179  *	%UNESCAPE_SPACE:
180  *		'\f' - form feed
181  *		'\n' - new line
182  *		'\r' - carriage return
183  *		'\t' - horizontal tab
184  *		'\v' - vertical tab
185  *	%UNESCAPE_OCTAL:
186  *		'\NNN' - byte with octal value NNN (1 to 3 digits)
187  *	%UNESCAPE_HEX:
188  *		'\xHH' - byte with hexadecimal value HH (1 to 2 digits)
189  *	%UNESCAPE_SPECIAL:
190  *		'\"' - double quote
191  *		'\\' - backslash
192  *		'\a' - alert (BEL)
193  *		'\e' - escape
194  *	%UNESCAPE_ANY:
195  *		all previous together
196  *
197  * Description:
198  * The function unquotes characters in the given string.
199  *
200  * Because the size of the output will be the same as or less than the size of
201  * the input, the transformation may be performed in place.
202  *
203  * Caller must provide valid source and destination pointers. Be aware that
204  * destination buffer will always be NULL-terminated. Source string must be
205  * NULL-terminated as well.
206  *
207  * Return:
208  * The amount of the characters processed to the destination buffer excluding
209  * trailing '\0' is returned.
210  */
211 int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
212 {
213 	char *out = dst;
214 
215 	while (*src && --size) {
216 		if (src[0] == '\\' && src[1] != '\0' && size > 1) {
217 			src++;
218 			size--;
219 
220 			if (flags & UNESCAPE_SPACE &&
221 					unescape_space(&src, &out))
222 				continue;
223 
224 			if (flags & UNESCAPE_OCTAL &&
225 					unescape_octal(&src, &out))
226 				continue;
227 
228 			if (flags & UNESCAPE_HEX &&
229 					unescape_hex(&src, &out))
230 				continue;
231 
232 			if (flags & UNESCAPE_SPECIAL &&
233 					unescape_special(&src, &out))
234 				continue;
235 
236 			*out++ = '\\';
237 		}
238 		*out++ = *src++;
239 	}
240 	*out = '\0';
241 
242 	return out - dst;
243 }
244 EXPORT_SYMBOL(string_unescape);
245 
246 static int escape_passthrough(unsigned char c, char **dst, size_t *osz)
247 {
248 	char *out = *dst;
249 
250 	if (*osz < 1)
251 		return -ENOMEM;
252 
253 	*out++ = c;
254 
255 	*dst = out;
256 	*osz -= 1;
257 
258 	return 1;
259 }
260 
261 static int escape_space(unsigned char c, char **dst, size_t *osz)
262 {
263 	char *out = *dst;
264 	unsigned char to;
265 
266 	if (*osz < 2)
267 		return -ENOMEM;
268 
269 	switch (c) {
270 	case '\n':
271 		to = 'n';
272 		break;
273 	case '\r':
274 		to = 'r';
275 		break;
276 	case '\t':
277 		to = 't';
278 		break;
279 	case '\v':
280 		to = 'v';
281 		break;
282 	case '\f':
283 		to = 'f';
284 		break;
285 	default:
286 		return 0;
287 	}
288 
289 	*out++ = '\\';
290 	*out++ = to;
291 
292 	*dst = out;
293 	*osz -= 2;
294 
295 	return 1;
296 }
297 
298 static int escape_special(unsigned char c, char **dst, size_t *osz)
299 {
300 	char *out = *dst;
301 	unsigned char to;
302 
303 	if (*osz < 2)
304 		return -ENOMEM;
305 
306 	switch (c) {
307 	case '\\':
308 		to = '\\';
309 		break;
310 	case '\a':
311 		to = 'a';
312 		break;
313 	case '\e':
314 		to = 'e';
315 		break;
316 	default:
317 		return 0;
318 	}
319 
320 	*out++ = '\\';
321 	*out++ = to;
322 
323 	*dst = out;
324 	*osz -= 2;
325 
326 	return 1;
327 }
328 
329 static int escape_null(unsigned char c, char **dst, size_t *osz)
330 {
331 	char *out = *dst;
332 
333 	if (*osz < 2)
334 		return -ENOMEM;
335 
336 	if (c)
337 		return 0;
338 
339 	*out++ = '\\';
340 	*out++ = '0';
341 
342 	*dst = out;
343 	*osz -= 2;
344 
345 	return 1;
346 }
347 
348 static int escape_octal(unsigned char c, char **dst, size_t *osz)
349 {
350 	char *out = *dst;
351 
352 	if (*osz < 4)
353 		return -ENOMEM;
354 
355 	*out++ = '\\';
356 	*out++ = ((c >> 6) & 0x07) + '0';
357 	*out++ = ((c >> 3) & 0x07) + '0';
358 	*out++ = ((c >> 0) & 0x07) + '0';
359 
360 	*dst = out;
361 	*osz -= 4;
362 
363 	return 1;
364 }
365 
366 static int escape_hex(unsigned char c, char **dst, size_t *osz)
367 {
368 	char *out = *dst;
369 
370 	if (*osz < 4)
371 		return -ENOMEM;
372 
373 	*out++ = '\\';
374 	*out++ = 'x';
375 	*out++ = hex_asc_hi(c);
376 	*out++ = hex_asc_lo(c);
377 
378 	*dst = out;
379 	*osz -= 4;
380 
381 	return 1;
382 }
383 
384 /**
385  * string_escape_mem - quote characters in the given memory buffer
386  * @src:	source buffer (unescaped)
387  * @isz:	source buffer size
388  * @dst:	destination buffer (escaped)
389  * @osz:	destination buffer size
390  * @flags:	combination of the flags (bitwise OR):
391  *	%ESCAPE_SPACE:
392  *		'\f' - form feed
393  *		'\n' - new line
394  *		'\r' - carriage return
395  *		'\t' - horizontal tab
396  *		'\v' - vertical tab
397  *	%ESCAPE_SPECIAL:
398  *		'\\' - backslash
399  *		'\a' - alert (BEL)
400  *		'\e' - escape
401  *	%ESCAPE_NULL:
402  *		'\0' - null
403  *	%ESCAPE_OCTAL:
404  *		'\NNN' - byte with octal value NNN (3 digits)
405  *	%ESCAPE_ANY:
406  *		all previous together
407  *	%ESCAPE_NP:
408  *		escape only non-printable characters (checked by isprint)
409  *	%ESCAPE_ANY_NP:
410  *		all previous together
411  *	%ESCAPE_HEX:
412  *		'\xHH' - byte with hexadecimal value HH (2 digits)
413  * @esc:	NULL-terminated string of characters any of which, if found in
414  *		the source, has to be escaped
415  *
416  * Description:
417  * The process of escaping byte buffer includes several parts. They are applied
418  * in the following sequence.
419  *	1. The character is matched to the printable class, if asked, and in
420  *	   case of match it passes through to the output.
421  *	2. The character is not matched to the one from @esc string and thus
422  *	   must go as is to the output.
423  *	3. The character is checked if it falls into the class given by @flags.
424  *	   %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any
425  *	   character. Note that they actually can't go together, otherwise
426  *	   %ESCAPE_HEX will be ignored.
427  *
428  * Caller must provide valid source and destination pointers. Be aware that
429  * destination buffer will not be NULL-terminated, thus caller have to append
430  * it if needs.
431  *
432  * Return:
433  * The amount of the characters processed to the destination buffer, or
434  * %-ENOMEM if the size of buffer is not enough to put an escaped character is
435  * returned.
436  *
437  * Even in the case of error @dst pointer will be updated to point to the byte
438  * after the last processed character.
439  */
440 int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz,
441 		      unsigned int flags, const char *esc)
442 {
443 	char *out = *dst, *p = out;
444 	bool is_dict = esc && *esc;
445 	int ret = 0;
446 
447 	while (isz--) {
448 		unsigned char c = *src++;
449 
450 		/*
451 		 * Apply rules in the following sequence:
452 		 *	- the character is printable, when @flags has
453 		 *	  %ESCAPE_NP bit set
454 		 *	- the @esc string is supplied and does not contain a
455 		 *	  character under question
456 		 *	- the character doesn't fall into a class of symbols
457 		 *	  defined by given @flags
458 		 * In these cases we just pass through a character to the
459 		 * output buffer.
460 		 */
461 		if ((flags & ESCAPE_NP && isprint(c)) ||
462 		    (is_dict && !strchr(esc, c))) {
463 			/* do nothing */
464 		} else {
465 			if (flags & ESCAPE_SPACE) {
466 				ret = escape_space(c, &p, &osz);
467 				if (ret < 0)
468 					break;
469 				if (ret > 0)
470 					continue;
471 			}
472 
473 			if (flags & ESCAPE_SPECIAL) {
474 				ret = escape_special(c, &p, &osz);
475 				if (ret < 0)
476 					break;
477 				if (ret > 0)
478 					continue;
479 			}
480 
481 			if (flags & ESCAPE_NULL) {
482 				ret = escape_null(c, &p, &osz);
483 				if (ret < 0)
484 					break;
485 				if (ret > 0)
486 					continue;
487 			}
488 
489 			/* ESCAPE_OCTAL and ESCAPE_HEX always go last */
490 			if (flags & ESCAPE_OCTAL) {
491 				ret = escape_octal(c, &p, &osz);
492 				if (ret < 0)
493 					break;
494 				continue;
495 			}
496 			if (flags & ESCAPE_HEX) {
497 				ret = escape_hex(c, &p, &osz);
498 				if (ret < 0)
499 					break;
500 				continue;
501 			}
502 		}
503 
504 		ret = escape_passthrough(c, &p, &osz);
505 		if (ret < 0)
506 			break;
507 	}
508 
509 	*dst = p;
510 
511 	if (ret < 0)
512 		return ret;
513 
514 	return p - out;
515 }
516 EXPORT_SYMBOL(string_escape_mem);
517